##// END OF EJS Templates
fix `--existing` with non-localhost IP...
MinRK -
Show More
@@ -1,392 +1,395
1 1 """ A minimal application base mixin for all ZMQ based IPython frontends.
2 2
3 3 This is not a complete console app, as subprocess will not be able to receive
4 4 input, there is no real readline support, among other limitations. This is a
5 5 refactoring of what used to be the IPython/qt/console/qtconsoleapp.py
6 6
7 7 Authors:
8 8
9 9 * Evan Patterson
10 10 * Min RK
11 11 * Erik Tollerud
12 12 * Fernando Perez
13 13 * Bussonnier Matthias
14 14 * Thomas Kluyver
15 15 * Paul Ivanov
16 16
17 17 """
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 # stdlib imports
24 24 import atexit
25 25 import json
26 26 import os
27 27 import shutil
28 28 import signal
29 29 import sys
30 30 import uuid
31 31
32 32
33 33 # Local imports
34 34 from IPython.config.application import boolean_flag
35 35 from IPython.config.configurable import Configurable
36 36 from IPython.core.profiledir import ProfileDir
37 37 from IPython.kernel.blocking import BlockingKernelClient
38 38 from IPython.kernel import KernelManager
39 39 from IPython.kernel import tunnel_to_kernel, find_connection_file, swallow_argv
40 40 from IPython.utils.path import filefind
41 41 from IPython.utils.py3compat import str_to_bytes
42 42 from IPython.utils.traitlets import (
43 43 Dict, List, Unicode, CUnicode, Int, CBool, Any, CaselessStrEnum
44 44 )
45 45 from IPython.kernel.zmq.kernelapp import (
46 46 kernel_flags,
47 47 kernel_aliases,
48 48 IPKernelApp
49 49 )
50 50 from IPython.kernel.zmq.session import Session, default_secure
51 51 from IPython.kernel.zmq.zmqshell import ZMQInteractiveShell
52 from IPython.kernel.connect import ConnectionFileMixin
52 53
53 54 #-----------------------------------------------------------------------------
54 55 # Network Constants
55 56 #-----------------------------------------------------------------------------
56 57
57 58 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
58 59
59 60 #-----------------------------------------------------------------------------
60 61 # Globals
61 62 #-----------------------------------------------------------------------------
62 63
63 64
64 65 #-----------------------------------------------------------------------------
65 66 # Aliases and Flags
66 67 #-----------------------------------------------------------------------------
67 68
68 69 flags = dict(kernel_flags)
69 70
70 71 # the flags that are specific to the frontend
71 72 # these must be scrubbed before being passed to the kernel,
72 73 # or it will raise an error on unrecognized flags
73 74 app_flags = {
74 75 'existing' : ({'IPythonConsoleApp' : {'existing' : 'kernel*.json'}},
75 76 "Connect to an existing kernel. If no argument specified, guess most recent"),
76 77 }
77 78 app_flags.update(boolean_flag(
78 79 'confirm-exit', 'IPythonConsoleApp.confirm_exit',
79 80 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
80 81 to force a direct exit without any confirmation.
81 82 """,
82 83 """Don't prompt the user when exiting. This will terminate the kernel
83 84 if it is owned by the frontend, and leave it alive if it is external.
84 85 """
85 86 ))
86 87 flags.update(app_flags)
87 88
88 89 aliases = dict(kernel_aliases)
89 90
90 91 # also scrub aliases from the frontend
91 92 app_aliases = dict(
92 ip = 'KernelManager.ip',
93 transport = 'KernelManager.transport',
93 ip = 'IPythonConsoleApp.ip',
94 transport = 'IPythonConsoleApp.transport',
94 95 hb = 'IPythonConsoleApp.hb_port',
95 96 shell = 'IPythonConsoleApp.shell_port',
96 97 iopub = 'IPythonConsoleApp.iopub_port',
97 98 stdin = 'IPythonConsoleApp.stdin_port',
98 99 existing = 'IPythonConsoleApp.existing',
99 100 f = 'IPythonConsoleApp.connection_file',
100 101
101 102
102 103 ssh = 'IPythonConsoleApp.sshserver',
103 104 )
104 105 aliases.update(app_aliases)
105 106
106 107 #-----------------------------------------------------------------------------
107 108 # Classes
108 109 #-----------------------------------------------------------------------------
109 110
110 111 #-----------------------------------------------------------------------------
111 112 # IPythonConsole
112 113 #-----------------------------------------------------------------------------
113 114
114 115 classes = [IPKernelApp, ZMQInteractiveShell, KernelManager, ProfileDir, Session]
115 116
116 117 try:
117 118 from IPython.kernel.zmq.pylab.backend_inline import InlineBackend
118 119 except ImportError:
119 120 pass
120 121 else:
121 122 classes.append(InlineBackend)
122 123
123 class IPythonConsoleApp(Configurable):
124 class IPythonConsoleApp(ConnectionFileMixin):
124 125 name = 'ipython-console-mixin'
125 126
126 127 description = """
127 128 The IPython Mixin Console.
128 129
129 130 This class contains the common portions of console client (QtConsole,
130 131 ZMQ-based terminal console, etc). It is not a full console, in that
131 132 launched terminal subprocesses will not be able to accept input.
132 133
133 134 The Console using this mixing supports various extra features beyond
134 135 the single-process Terminal IPython shell, such as connecting to
135 136 existing kernel, via:
136 137
137 138 ipython <appname> --existing
138 139
139 140 as well as tunnel via SSH
140 141
141 142 """
142 143
143 144 classes = classes
144 145 flags = Dict(flags)
145 146 aliases = Dict(aliases)
146 147 kernel_manager_class = KernelManager
147 148 kernel_client_class = BlockingKernelClient
148 149
149 150 kernel_argv = List(Unicode)
150 151 # frontend flags&aliases to be stripped when building kernel_argv
151 152 frontend_flags = Any(app_flags)
152 153 frontend_aliases = Any(app_aliases)
153 154
154 155 # create requested profiles by default, if they don't exist:
155 156 auto_create = CBool(True)
156 157 # connection info:
157 158
158 159 sshserver = Unicode('', config=True,
159 160 help="""The SSH server to use to connect to the kernel.""")
160 161 sshkey = Unicode('', config=True,
161 162 help="""Path to the ssh key to use for logging in to the ssh server.""")
162 163
163 164 hb_port = Int(0, config=True,
164 165 help="set the heartbeat port [default: random]")
165 166 shell_port = Int(0, config=True,
166 167 help="set the shell (ROUTER) port [default: random]")
167 168 iopub_port = Int(0, config=True,
168 169 help="set the iopub (PUB) port [default: random]")
169 170 stdin_port = Int(0, config=True,
170 171 help="set the stdin (DEALER) port [default: random]")
171 172 connection_file = Unicode('', config=True,
172 173 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
173 174
174 175 This file will contain the IP, ports, and authentication key needed to connect
175 176 clients to this kernel. By default, this file will be created in the security-dir
176 177 of the current profile, but can be specified by absolute path.
177 178 """)
178 179 def _connection_file_default(self):
179 180 return 'kernel-%i.json' % os.getpid()
180 181
181 182 existing = CUnicode('', config=True,
182 183 help="""Connect to an already running kernel""")
183 184
184 185 confirm_exit = CBool(True, config=True,
185 186 help="""
186 187 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
187 188 to force a direct exit without any confirmation.""",
188 189 )
189 190
190 191
191 192 def build_kernel_argv(self, argv=None):
192 193 """build argv to be passed to kernel subprocess"""
193 194 if argv is None:
194 195 argv = sys.argv[1:]
195 196 self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags)
196 197 # kernel should inherit default config file from frontend
197 198 self.kernel_argv.append("--IPKernelApp.parent_appname='%s'" % self.name)
198 199
199 200 def init_connection_file(self):
200 201 """find the connection file, and load the info if found.
201 202
202 203 The current working directory and the current profile's security
203 204 directory will be searched for the file if it is not given by
204 205 absolute path.
205 206
206 207 When attempting to connect to an existing kernel and the `--existing`
207 208 argument does not match an existing file, it will be interpreted as a
208 209 fileglob, and the matching file in the current profile's security dir
209 210 with the latest access time will be used.
210 211
211 212 After this method is called, self.connection_file contains the *full path*
212 213 to the connection file, never just its name.
213 214 """
214 215 if self.existing:
215 216 try:
216 217 cf = find_connection_file(self.existing)
217 218 except Exception:
218 219 self.log.critical("Could not find existing kernel connection file %s", self.existing)
219 220 self.exit(1)
220 221 self.log.info("Connecting to existing kernel: %s" % cf)
221 222 self.connection_file = cf
222 223 else:
223 224 # not existing, check if we are going to write the file
224 225 # and ensure that self.connection_file is a full path, not just the shortname
225 226 try:
226 227 cf = find_connection_file(self.connection_file)
227 228 except Exception:
228 229 # file might not exist
229 230 if self.connection_file == os.path.basename(self.connection_file):
230 231 # just shortname, put it in security dir
231 232 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
232 233 else:
233 234 cf = self.connection_file
234 235 self.connection_file = cf
235 236
236 237 # should load_connection_file only be used for existing?
237 238 # as it is now, this allows reusing ports if an existing
238 239 # file is requested
239 240 try:
240 241 self.load_connection_file()
241 242 except Exception:
242 243 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
243 244 self.exit(1)
244 245
245 246 def load_connection_file(self):
246 247 """load ip/port/hmac config from JSON connection file"""
247 248 # this is identical to IPKernelApp.load_connection_file
248 249 # perhaps it can be centralized somewhere?
249 250 try:
250 251 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
251 252 except IOError:
252 253 self.log.debug("Connection File not found: %s", self.connection_file)
253 254 return
254 255 self.log.debug(u"Loading connection file %s", fname)
255 256 with open(fname) as f:
256 257 cfg = json.load(f)
257
258 self.config.KernelManager.transport = cfg.get('transport', 'tcp')
259 self.config.KernelManager.ip = cfg.get('ip', LOCALHOST)
258 self.transport = cfg.get('transport', 'tcp')
259 self.ip = cfg.get('ip', LOCALHOST)
260 260
261 261 for channel in ('hb', 'shell', 'iopub', 'stdin'):
262 262 name = channel + '_port'
263 263 if getattr(self, name) == 0 and name in cfg:
264 264 # not overridden by config or cl_args
265 265 setattr(self, name, cfg[name])
266 266 if 'key' in cfg:
267 267 self.config.Session.key = str_to_bytes(cfg['key'])
268 268 if 'signature_scheme' in cfg:
269 269 self.config.Session.signature_scheme = cfg['signature_scheme']
270 270
271 271 def init_ssh(self):
272 272 """set up ssh tunnels, if needed."""
273 273 if not self.existing or (not self.sshserver and not self.sshkey):
274 274 return
275
276 275 self.load_connection_file()
277 276
278 transport = self.config.KernelManager.transport
279 ip = self.config.KernelManager.ip
277 transport = self.transport
278 ip = self.ip
280 279
281 280 if transport != 'tcp':
282 281 self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport)
283 282 sys.exit(-1)
284 283
285 284 if self.sshkey and not self.sshserver:
286 285 # specifying just the key implies that we are connecting directly
287 286 self.sshserver = ip
288 287 ip = LOCALHOST
289 288
290 289 # build connection dict for tunnels:
291 290 info = dict(ip=ip,
292 291 shell_port=self.shell_port,
293 292 iopub_port=self.iopub_port,
294 293 stdin_port=self.stdin_port,
295 294 hb_port=self.hb_port
296 295 )
297 296
298 297 self.log.info("Forwarding connections to %s via %s"%(ip, self.sshserver))
299 298
300 299 # tunnels return a new set of ports, which will be on localhost:
301 self.config.KernelManager.ip = LOCALHOST
300 self.ip = LOCALHOST
302 301 try:
303 302 newports = tunnel_to_kernel(info, self.sshserver, self.sshkey)
304 303 except:
305 304 # even catch KeyboardInterrupt
306 305 self.log.error("Could not setup tunnels", exc_info=True)
307 306 self.exit(1)
308 307
309 308 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports
310 309
311 310 cf = self.connection_file
312 311 base,ext = os.path.splitext(cf)
313 312 base = os.path.basename(base)
314 313 self.connection_file = os.path.basename(base)+'-ssh'+ext
315 314 self.log.critical("To connect another client via this tunnel, use:")
316 315 self.log.critical("--existing %s" % self.connection_file)
317 316
318 317 def _new_connection_file(self):
319 318 cf = ''
320 319 while not cf:
321 320 # we don't need a 128b id to distinguish kernels, use more readable
322 321 # 48b node segment (12 hex chars). Users running more than 32k simultaneous
323 322 # kernels can subclass.
324 323 ident = str(uuid.uuid4()).split('-')[-1]
325 324 cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident)
326 325 # only keep if it's actually new. Protect against unlikely collision
327 326 # in 48b random search space
328 327 cf = cf if not os.path.exists(cf) else ''
329 328 return cf
330 329
331 330 def init_kernel_manager(self):
332 331 # Don't let Qt or ZMQ swallow KeyboardInterupts.
333 332 if self.existing:
334 333 self.kernel_manager = None
335 334 return
336 335 signal.signal(signal.SIGINT, signal.SIG_DFL)
337 336
338 337 # Create a KernelManager and start a kernel.
339 338 self.kernel_manager = self.kernel_manager_class(
339 ip=self.ip,
340 transport=self.transport,
340 341 shell_port=self.shell_port,
341 342 iopub_port=self.iopub_port,
342 343 stdin_port=self.stdin_port,
343 344 hb_port=self.hb_port,
344 345 connection_file=self.connection_file,
345 346 parent=self,
346 347 )
347 348 self.kernel_manager.client_factory = self.kernel_client_class
348 349 self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv)
349 350 atexit.register(self.kernel_manager.cleanup_ipc_files)
350 351
351 352 if self.sshserver:
352 353 # ssh, write new connection file
353 354 self.kernel_manager.write_connection_file()
354 355
355 356 # in case KM defaults / ssh writing changes things:
356 357 km = self.kernel_manager
357 358 self.shell_port=km.shell_port
358 359 self.iopub_port=km.iopub_port
359 360 self.stdin_port=km.stdin_port
360 361 self.hb_port=km.hb_port
361 362 self.connection_file = km.connection_file
362 363
363 364 atexit.register(self.kernel_manager.cleanup_connection_file)
364 365
365 366 def init_kernel_client(self):
366 367 if self.kernel_manager is not None:
367 368 self.kernel_client = self.kernel_manager.client()
368 369 else:
369 370 self.kernel_client = self.kernel_client_class(
371 ip=self.ip,
372 transport=self.transport,
370 373 shell_port=self.shell_port,
371 374 iopub_port=self.iopub_port,
372 375 stdin_port=self.stdin_port,
373 376 hb_port=self.hb_port,
374 377 connection_file=self.connection_file,
375 378 parent=self,
376 379 )
377 380
378 381 self.kernel_client.start_channels()
379 382
380 383
381 384
382 385 def initialize(self, argv=None):
383 386 """
384 387 Classes which mix this class in should call:
385 388 IPythonConsoleApp.initialize(self,argv)
386 389 """
387 390 self.init_connection_file()
388 391 default_secure(self.config)
389 392 self.init_ssh()
390 393 self.init_kernel_manager()
391 394 self.init_kernel_client()
392 395
@@ -1,385 +1,382
1 1 """ A minimal application using the Qt console-style IPython frontend.
2 2
3 3 This is not a complete console app, as subprocess will not be able to receive
4 4 input, there is no real readline support, among other limitations.
5 5
6 6 Authors:
7 7
8 8 * Evan Patterson
9 9 * Min RK
10 10 * Erik Tollerud
11 11 * Fernando Perez
12 12 * Bussonnier Matthias
13 13 * Thomas Kluyver
14 14 * Paul Ivanov
15 15
16 16 """
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 # stdlib imports
23 23 import os
24 24 import signal
25 25 import sys
26 26
27 27 # If run on Windows, install an exception hook which pops up a
28 28 # message box. Pythonw.exe hides the console, so without this
29 29 # the application silently fails to load.
30 30 #
31 31 # We always install this handler, because the expectation is for
32 32 # qtconsole to bring up a GUI even if called from the console.
33 33 # The old handler is called, so the exception is printed as well.
34 34 # If desired, check for pythonw with an additional condition
35 35 # (sys.executable.lower().find('pythonw.exe') >= 0).
36 36 if os.name == 'nt':
37 37 old_excepthook = sys.excepthook
38 38
39 39 def gui_excepthook(exctype, value, tb):
40 40 try:
41 41 import ctypes, traceback
42 42 MB_ICONERROR = 0x00000010L
43 43 title = u'Error starting IPython QtConsole'
44 44 msg = u''.join(traceback.format_exception(exctype, value, tb))
45 45 ctypes.windll.user32.MessageBoxW(0, msg, title, MB_ICONERROR)
46 46 finally:
47 47 # Also call the old exception hook to let it do
48 48 # its thing too.
49 49 old_excepthook(exctype, value, tb)
50 50
51 51 sys.excepthook = gui_excepthook
52 52
53 53 # System library imports
54 54 from IPython.external.qt import QtCore, QtGui
55 55
56 56 # Local imports
57 57 from IPython.config.application import catch_config_error
58 58 from IPython.core.application import BaseIPythonApplication
59 59 from IPython.qt.console.ipython_widget import IPythonWidget
60 60 from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
61 61 from IPython.qt.console import styles
62 62 from IPython.qt.console.mainwindow import MainWindow
63 63 from IPython.qt.client import QtKernelClient
64 64 from IPython.qt.manager import QtKernelManager
65 65 from IPython.utils.traitlets import (
66 66 Dict, Unicode, CBool, Any
67 67 )
68 68
69 69 from IPython.consoleapp import (
70 70 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
71 71 )
72 72
73 73 #-----------------------------------------------------------------------------
74 74 # Network Constants
75 75 #-----------------------------------------------------------------------------
76 76
77 77 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
78 78
79 79 #-----------------------------------------------------------------------------
80 80 # Globals
81 81 #-----------------------------------------------------------------------------
82 82
83 83 _examples = """
84 84 ipython qtconsole # start the qtconsole
85 85 ipython qtconsole --matplotlib=inline # start with matplotlib inline plotting mode
86 86 """
87 87
88 88 #-----------------------------------------------------------------------------
89 89 # Aliases and Flags
90 90 #-----------------------------------------------------------------------------
91 91
92 92 # start with copy of flags
93 93 flags = dict(flags)
94 94 qt_flags = {
95 95 'plain' : ({'IPythonQtConsoleApp' : {'plain' : True}},
96 96 "Disable rich text support."),
97 97 }
98 98
99 99 # and app_flags from the Console Mixin
100 100 qt_flags.update(app_flags)
101 101 # add frontend flags to the full set
102 102 flags.update(qt_flags)
103 103
104 104 # start with copy of front&backend aliases list
105 105 aliases = dict(aliases)
106 106 qt_aliases = dict(
107 107 style = 'IPythonWidget.syntax_style',
108 108 stylesheet = 'IPythonQtConsoleApp.stylesheet',
109 109 colors = 'ZMQInteractiveShell.colors',
110 110
111 111 editor = 'IPythonWidget.editor',
112 112 paging = 'ConsoleWidget.paging',
113 113 )
114 114 # and app_aliases from the Console Mixin
115 115 qt_aliases.update(app_aliases)
116 116 qt_aliases.update({'gui-completion':'ConsoleWidget.gui_completion'})
117 117 # add frontend aliases to the full set
118 118 aliases.update(qt_aliases)
119 119
120 120 # get flags&aliases into sets, and remove a couple that
121 121 # shouldn't be scrubbed from backend flags:
122 122 qt_aliases = set(qt_aliases.keys())
123 123 qt_aliases.remove('colors')
124 124 qt_flags = set(qt_flags.keys())
125 125
126 126 #-----------------------------------------------------------------------------
127 127 # Classes
128 128 #-----------------------------------------------------------------------------
129 129
130 130 #-----------------------------------------------------------------------------
131 131 # IPythonQtConsole
132 132 #-----------------------------------------------------------------------------
133 133
134 134
135 135 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
136 136 name = 'ipython-qtconsole'
137 137
138 138 description = """
139 139 The IPython QtConsole.
140 140
141 141 This launches a Console-style application using Qt. It is not a full
142 142 console, in that launched terminal subprocesses will not be able to accept
143 143 input.
144 144
145 145 The QtConsole supports various extra features beyond the Terminal IPython
146 146 shell, such as inline plotting with matplotlib, via:
147 147
148 148 ipython qtconsole --matplotlib=inline
149 149
150 150 as well as saving your session as HTML, and printing the output.
151 151
152 152 """
153 153 examples = _examples
154 154
155 155 classes = [IPythonWidget] + IPythonConsoleApp.classes
156 156 flags = Dict(flags)
157 157 aliases = Dict(aliases)
158 158 frontend_flags = Any(qt_flags)
159 159 frontend_aliases = Any(qt_aliases)
160 160 kernel_client_class = QtKernelClient
161 161 kernel_manager_class = QtKernelManager
162 162
163 163 stylesheet = Unicode('', config=True,
164 164 help="path to a custom CSS stylesheet")
165 165
166 166 hide_menubar = CBool(False, config=True,
167 167 help="Start the console window with the menu bar hidden.")
168 168
169 169 maximize = CBool(False, config=True,
170 170 help="Start the console window maximized.")
171 171
172 172 plain = CBool(False, config=True,
173 173 help="Use a plaintext widget instead of rich text (plain can't print/save).")
174 174
175 175 def _plain_changed(self, name, old, new):
176 176 kind = 'plain' if new else 'rich'
177 177 self.config.ConsoleWidget.kind = kind
178 178 if new:
179 179 self.widget_factory = IPythonWidget
180 180 else:
181 181 self.widget_factory = RichIPythonWidget
182 182
183 183 # the factory for creating a widget
184 184 widget_factory = Any(RichIPythonWidget)
185 185
186 186 def parse_command_line(self, argv=None):
187 187 super(IPythonQtConsoleApp, self).parse_command_line(argv)
188 188 self.build_kernel_argv(argv)
189 189
190 190
191 191 def new_frontend_master(self):
192 192 """ Create and return new frontend attached to new kernel, launched on localhost.
193 193 """
194 194 kernel_manager = self.kernel_manager_class(
195 195 connection_file=self._new_connection_file(),
196 196 parent=self,
197 197 autorestart=True,
198 198 )
199 199 # start the kernel
200 200 kwargs = dict()
201 201 kwargs['extra_arguments'] = self.kernel_argv
202 202 kernel_manager.start_kernel(**kwargs)
203 203 kernel_manager.client_factory = self.kernel_client_class
204 204 kernel_client = kernel_manager.client()
205 205 kernel_client.start_channels(shell=True, iopub=True)
206 206 widget = self.widget_factory(config=self.config,
207 207 local_kernel=True)
208 208 self.init_colors(widget)
209 209 widget.kernel_manager = kernel_manager
210 210 widget.kernel_client = kernel_client
211 211 widget._existing = False
212 212 widget._may_close = True
213 213 widget._confirm_exit = self.confirm_exit
214 214 return widget
215 215
216 216 def new_frontend_slave(self, current_widget):
217 217 """Create and return a new frontend attached to an existing kernel.
218 218
219 219 Parameters
220 220 ----------
221 221 current_widget : IPythonWidget
222 222 The IPythonWidget whose kernel this frontend is to share
223 223 """
224 224 kernel_client = self.kernel_client_class(
225 225 connection_file=current_widget.kernel_client.connection_file,
226 226 config = self.config,
227 227 )
228 228 kernel_client.load_connection_file()
229 229 kernel_client.start_channels()
230 230 widget = self.widget_factory(config=self.config,
231 231 local_kernel=False)
232 232 self.init_colors(widget)
233 233 widget._existing = True
234 234 widget._may_close = False
235 235 widget._confirm_exit = False
236 236 widget.kernel_client = kernel_client
237 237 widget.kernel_manager = current_widget.kernel_manager
238 238 return widget
239 239
240 240 def init_qt_app(self):
241 241 # separate from qt_elements, because it must run first
242 242 self.app = QtGui.QApplication([])
243 243
244 244 def init_qt_elements(self):
245 245 # Create the widget.
246 246
247 247 base_path = os.path.abspath(os.path.dirname(__file__))
248 248 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
249 249 self.app.icon = QtGui.QIcon(icon_path)
250 250 QtGui.QApplication.setWindowIcon(self.app.icon)
251 251
252 try:
253 ip = self.config.KernelManager.ip
254 except AttributeError:
255 ip = LOCALHOST
252 ip = self.ip
256 253 local_kernel = (not self.existing) or ip in LOCAL_IPS
257 254 self.widget = self.widget_factory(config=self.config,
258 255 local_kernel=local_kernel)
259 256 self.init_colors(self.widget)
260 257 self.widget._existing = self.existing
261 258 self.widget._may_close = not self.existing
262 259 self.widget._confirm_exit = self.confirm_exit
263 260
264 261 self.widget.kernel_manager = self.kernel_manager
265 262 self.widget.kernel_client = self.kernel_client
266 263 self.window = MainWindow(self.app,
267 264 confirm_exit=self.confirm_exit,
268 265 new_frontend_factory=self.new_frontend_master,
269 266 slave_frontend_factory=self.new_frontend_slave,
270 267 )
271 268 self.window.log = self.log
272 269 self.window.add_tab_with_frontend(self.widget)
273 270 self.window.init_menu_bar()
274 271
275 272 # Ignore on OSX, where there is always a menu bar
276 273 if sys.platform != 'darwin' and self.hide_menubar:
277 274 self.window.menuBar().setVisible(False)
278 275
279 276 self.window.setWindowTitle('IPython')
280 277
281 278 def init_colors(self, widget):
282 279 """Configure the coloring of the widget"""
283 280 # Note: This will be dramatically simplified when colors
284 281 # are removed from the backend.
285 282
286 283 # parse the colors arg down to current known labels
287 284 try:
288 285 colors = self.config.ZMQInteractiveShell.colors
289 286 except AttributeError:
290 287 colors = None
291 288 try:
292 289 style = self.config.IPythonWidget.syntax_style
293 290 except AttributeError:
294 291 style = None
295 292 try:
296 293 sheet = self.config.IPythonWidget.style_sheet
297 294 except AttributeError:
298 295 sheet = None
299 296
300 297 # find the value for colors:
301 298 if colors:
302 299 colors=colors.lower()
303 300 if colors in ('lightbg', 'light'):
304 301 colors='lightbg'
305 302 elif colors in ('dark', 'linux'):
306 303 colors='linux'
307 304 else:
308 305 colors='nocolor'
309 306 elif style:
310 307 if style=='bw':
311 308 colors='nocolor'
312 309 elif styles.dark_style(style):
313 310 colors='linux'
314 311 else:
315 312 colors='lightbg'
316 313 else:
317 314 colors=None
318 315
319 316 # Configure the style
320 317 if style:
321 318 widget.style_sheet = styles.sheet_from_template(style, colors)
322 319 widget.syntax_style = style
323 320 widget._syntax_style_changed()
324 321 widget._style_sheet_changed()
325 322 elif colors:
326 323 # use a default dark/light/bw style
327 324 widget.set_default_style(colors=colors)
328 325
329 326 if self.stylesheet:
330 327 # we got an explicit stylesheet
331 328 if os.path.isfile(self.stylesheet):
332 329 with open(self.stylesheet) as f:
333 330 sheet = f.read()
334 331 else:
335 332 raise IOError("Stylesheet %r not found." % self.stylesheet)
336 333 if sheet:
337 334 widget.style_sheet = sheet
338 335 widget._style_sheet_changed()
339 336
340 337
341 338 def init_signal(self):
342 339 """allow clean shutdown on sigint"""
343 340 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
344 341 # need a timer, so that QApplication doesn't block until a real
345 342 # Qt event fires (can require mouse movement)
346 343 # timer trick from http://stackoverflow.com/q/4938723/938949
347 344 timer = QtCore.QTimer()
348 345 # Let the interpreter run each 200 ms:
349 346 timer.timeout.connect(lambda: None)
350 347 timer.start(200)
351 348 # hold onto ref, so the timer doesn't get cleaned up
352 349 self._sigint_timer = timer
353 350
354 351 @catch_config_error
355 352 def initialize(self, argv=None):
356 353 self.init_qt_app()
357 354 super(IPythonQtConsoleApp, self).initialize(argv)
358 355 IPythonConsoleApp.initialize(self,argv)
359 356 self.init_qt_elements()
360 357 self.init_signal()
361 358
362 359 def start(self):
363 360
364 361 # draw the window
365 362 if self.maximize:
366 363 self.window.showMaximized()
367 364 else:
368 365 self.window.show()
369 366 self.window.raise_()
370 367
371 368 # Start the application main loop.
372 369 self.app.exec_()
373 370
374 371 #-----------------------------------------------------------------------------
375 372 # Main entry point
376 373 #-----------------------------------------------------------------------------
377 374
378 375 def main():
379 376 app = IPythonQtConsoleApp()
380 377 app.initialize()
381 378 app.start()
382 379
383 380
384 381 if __name__ == '__main__':
385 382 main()
General Comments 0
You need to be logged in to leave comments. Login now