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