##// END OF EJS Templates
fix qtconsole description...
MinRK -
Show More
@@ -1,434 +1,442
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
180 180 flags.update({
181 181 'existing' : ({'IPythonQtConsoleApp' : {'existing' : True}},
182 182 "Connect to an existing kernel."),
183 183 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
184 184 "Use a pure Python kernel instead of an IPython kernel."),
185 185 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
186 186 "Disable rich text support."),
187 187 })
188 188 flags.update(boolean_flag(
189 189 'gui-completion', 'ConsoleWidget.gui_completion',
190 190 "use a GUI widget for tab completion",
191 191 "use plaintext output for completion"
192 192 ))
193 193 flags.update(boolean_flag(
194 194 'confirm-exit', 'IPythonQtConsoleApp.confirm_exit',
195 195 """Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
196 196 to force a direct exit without any confirmation.
197 197 """,
198 198 """Don't prompt the user when exiting. This will terminate the kernel
199 199 if it is owned by the frontend, and leave it alive if it is external.
200 200 """
201 201 ))
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 = ['existing', 'pure', 'plain', 'gui-completion', 'no-gui-completion',
206 206 'confirm-exit', 'no-confirm-exit']
207 207
208 208 aliases = dict(ipkernel_aliases)
209 209
210 210 aliases.update(dict(
211 211 hb = 'IPythonQtConsoleApp.hb_port',
212 212 shell = 'IPythonQtConsoleApp.shell_port',
213 213 iopub = 'IPythonQtConsoleApp.iopub_port',
214 214 stdin = 'IPythonQtConsoleApp.stdin_port',
215 215 ip = 'IPythonQtConsoleApp.ip',
216 216
217 217 plain = 'IPythonQtConsoleApp.plain',
218 218 pure = 'IPythonQtConsoleApp.pure',
219 gui_completion = 'ConsoleWidget.gui_completion',
220 219 style = 'IPythonWidget.syntax_style',
221 220 stylesheet = 'IPythonQtConsoleApp.stylesheet',
222 221 colors = 'ZMQInteractiveShell.colors',
223 222
224 223 editor = 'IPythonWidget.editor',
224 paging = 'ConsoleWidget.paging',
225 225 ))
226 aliases['gui-completion'] = 'ConsoleWidget.gui_completion'
227
226 228
227 229 #-----------------------------------------------------------------------------
228 230 # IPythonQtConsole
229 231 #-----------------------------------------------------------------------------
230 232
231 233
232 234 class IPythonQtConsoleApp(BaseIPythonApplication):
233 235 name = 'ipython-qtconsole'
234 236 default_config_file_name='ipython_config.py'
235 237
236 238 description = """
237 239 The IPython QtConsole.
238 240
239 241 This launches a Console-style application using Qt. It is not a full
240 console, in that launched terminal subprocesses will not.
242 console, in that launched terminal subprocesses will not be able to accept
243 input.
244
245 The QtConsole supports various extra features beyond the Terminal IPython
246 shell, such as inline plotting with matplotlib, via:
247
248 ipython qtconsole --pylab=inline
241 249
242 The QtConsole supports various extra features beyond the
250 as well as saving your session as HTML, and printing the output.
243 251
244 252 """
245 253 examples = _examples
246 254
247 255 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
248 256 flags = Dict(flags)
249 257 aliases = Dict(aliases)
250 258
251 259 kernel_argv = List(Unicode)
252 260
253 261 # connection info:
254 262 ip = Unicode(LOCALHOST, config=True,
255 263 help="""Set the kernel\'s IP address [default localhost].
256 264 If the IP address is something other than localhost, then
257 265 Consoles on other machines will be able to connect
258 266 to the Kernel, so be careful!"""
259 267 )
260 268 hb_port = Int(0, config=True,
261 269 help="set the heartbeat port [default: random]")
262 270 shell_port = Int(0, config=True,
263 271 help="set the shell (XREP) port [default: random]")
264 272 iopub_port = Int(0, config=True,
265 273 help="set the iopub (PUB) port [default: random]")
266 274 stdin_port = Int(0, config=True,
267 275 help="set the stdin (XREQ) port [default: random]")
268 276
269 277 existing = CBool(False, config=True,
270 278 help="Whether to connect to an already running Kernel.")
271 279
272 280 stylesheet = Unicode('', config=True,
273 281 help="path to a custom CSS stylesheet")
274 282
275 283 pure = CBool(False, config=True,
276 284 help="Use a pure Python kernel instead of an IPython kernel.")
277 285 plain = CBool(False, config=True,
278 286 help="Use a plaintext widget instead of rich text (plain can't print/save).")
279 287
280 288 def _pure_changed(self, name, old, new):
281 289 kind = 'plain' if self.plain else 'rich'
282 290 self.config.ConsoleWidget.kind = kind
283 291 if self.pure:
284 292 self.widget_factory = FrontendWidget
285 293 elif self.plain:
286 294 self.widget_factory = IPythonWidget
287 295 else:
288 296 self.widget_factory = RichIPythonWidget
289 297
290 298 _plain_changed = _pure_changed
291 299
292 300 confirm_exit = CBool(True, config=True,
293 301 help="""
294 302 Set to display confirmation dialog on exit. You can always use 'exit' or 'quit',
295 303 to force a direct exit without any confirmation.""",
296 304 )
297 305
298 306 # the factory for creating a widget
299 307 widget_factory = Any(RichIPythonWidget)
300 308
301 309 def parse_command_line(self, argv=None):
302 310 super(IPythonQtConsoleApp, self).parse_command_line(argv)
303 311 if argv is None:
304 312 argv = sys.argv[1:]
305 313
306 314 self.kernel_argv = list(argv) # copy
307 315 # kernel should inherit default config file from frontend
308 316 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
309 317 # scrub frontend-specific flags
310 318 for a in argv:
311 319 if a.startswith('-') and a.lstrip('-') in qt_flags:
312 320 self.kernel_argv.remove(a)
313 321
314 322 def init_kernel_manager(self):
315 323 # Don't let Qt or ZMQ swallow KeyboardInterupts.
316 324 signal.signal(signal.SIGINT, signal.SIG_DFL)
317 325
318 326 # Create a KernelManager and start a kernel.
319 327 self.kernel_manager = QtKernelManager(
320 328 shell_address=(self.ip, self.shell_port),
321 329 sub_address=(self.ip, self.iopub_port),
322 330 stdin_address=(self.ip, self.stdin_port),
323 331 hb_address=(self.ip, self.hb_port),
324 332 config=self.config
325 333 )
326 334 # start the kernel
327 335 if not self.existing:
328 336 kwargs = dict(ip=self.ip, ipython=not self.pure)
329 337 kwargs['extra_arguments'] = self.kernel_argv
330 338 self.kernel_manager.start_kernel(**kwargs)
331 339 self.kernel_manager.start_channels()
332 340
333 341
334 342 def init_qt_elements(self):
335 343 # Create the widget.
336 344 self.app = QtGui.QApplication([])
337 345 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
338 346 self.widget = self.widget_factory(config=self.config,
339 347 local_kernel=local_kernel)
340 348 self.widget.kernel_manager = self.kernel_manager
341 349 self.window = MainWindow(self.app, self.widget, self.existing,
342 350 may_close=local_kernel,
343 351 confirm_exit=self.confirm_exit)
344 352 self.window.setWindowTitle('Python' if self.pure else 'IPython')
345 353
346 354 def init_colors(self):
347 355 """Configure the coloring of the widget"""
348 356 # Note: This will be dramatically simplified when colors
349 357 # are removed from the backend.
350 358
351 359 if self.pure:
352 360 # only IPythonWidget supports styling
353 361 return
354 362
355 363 # parse the colors arg down to current known labels
356 364 try:
357 365 colors = self.config.ZMQInteractiveShell.colors
358 366 except AttributeError:
359 367 colors = None
360 368 try:
361 369 style = self.config.IPythonWidget.colors
362 370 except AttributeError:
363 371 style = None
364 372
365 373 # find the value for colors:
366 374 if colors:
367 375 colors=colors.lower()
368 376 if colors in ('lightbg', 'light'):
369 377 colors='lightbg'
370 378 elif colors in ('dark', 'linux'):
371 379 colors='linux'
372 380 else:
373 381 colors='nocolor'
374 382 elif style:
375 383 if style=='bw':
376 384 colors='nocolor'
377 385 elif styles.dark_style(style):
378 386 colors='linux'
379 387 else:
380 388 colors='lightbg'
381 389 else:
382 390 colors=None
383 391
384 392 # Configure the style.
385 393 widget = self.widget
386 394 if style:
387 395 widget.style_sheet = styles.sheet_from_template(style, colors)
388 396 widget.syntax_style = style
389 397 widget._syntax_style_changed()
390 398 widget._style_sheet_changed()
391 399 elif colors:
392 400 # use a default style
393 401 widget.set_default_style(colors=colors)
394 402 else:
395 403 # this is redundant for now, but allows the widget's
396 404 # defaults to change
397 405 widget.set_default_style()
398 406
399 407 if self.stylesheet:
400 408 # we got an expicit stylesheet
401 409 if os.path.isfile(self.stylesheet):
402 410 with open(self.stylesheet) as f:
403 411 sheet = f.read()
404 412 widget.style_sheet = sheet
405 413 widget._style_sheet_changed()
406 414 else:
407 415 raise IOError("Stylesheet %r not found."%self.stylesheet)
408 416
409 417 def initialize(self, argv=None):
410 418 super(IPythonQtConsoleApp, self).initialize(argv)
411 419 self.init_kernel_manager()
412 420 self.init_qt_elements()
413 421 self.init_colors()
414 422
415 423 def start(self):
416 424
417 425 # draw the window
418 426 self.window.show()
419 427
420 428 # Start the application main loop.
421 429 self.app.exec_()
422 430
423 431 #-----------------------------------------------------------------------------
424 432 # Main entry point
425 433 #-----------------------------------------------------------------------------
426 434
427 435 def main():
428 436 app = IPythonQtConsoleApp()
429 437 app.initialize()
430 438 app.start()
431 439
432 440
433 441 if __name__ == '__main__':
434 442 main()
General Comments 0
You need to be logged in to leave comments. Login now