##// END OF EJS Templates
Updated file header according to IPython Copyright Policy
Dimitry Kloper -
Show More
@@ -1,379 +1,382 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 * Bussonnier Matthias
13 13 * Thomas Kluyver
14 14 * Paul Ivanov
15 15
16 16 """
17 17
18 # Copyright (c) IPython Development Team.
19 # Distributed under the terms of the Modified BSD License.
20
18 21 #-----------------------------------------------------------------------------
19 22 # Imports
20 23 #-----------------------------------------------------------------------------
21 24
22 25 # stdlib imports
23 26 import os
24 27 import signal
25 28 import sys
26 29
27 30 # If run on Windows, install an exception hook which pops up a
28 31 # message box. Pythonw.exe hides the console, so without this
29 32 # the application silently fails to load.
30 33 #
31 34 # We always install this handler, because the expectation is for
32 35 # qtconsole to bring up a GUI even if called from the console.
33 36 # The old handler is called, so the exception is printed as well.
34 37 # If desired, check for pythonw with an additional condition
35 38 # (sys.executable.lower().find('pythonw.exe') >= 0).
36 39 if os.name == 'nt':
37 40 old_excepthook = sys.excepthook
38 41
39 42 # Exclude this from our autogenerated API docs.
40 43 undoc = lambda func: func
41 44
42 45 @undoc
43 46 def gui_excepthook(exctype, value, tb):
44 47 try:
45 48 import ctypes, traceback
46 49 MB_ICONERROR = 0x00000010
47 50 title = u'Error starting IPython QtConsole'
48 51 msg = u''.join(traceback.format_exception(exctype, value, tb))
49 52 ctypes.windll.user32.MessageBoxW(0, msg, title, MB_ICONERROR)
50 53 finally:
51 54 # Also call the old exception hook to let it do
52 55 # its thing too.
53 56 old_excepthook(exctype, value, tb)
54 57
55 58 sys.excepthook = gui_excepthook
56 59
57 60 # System library imports
58 61 from IPython.external.qt import QtCore, QtGui
59 62
60 63 # Local imports
61 64 from IPython.config.application import catch_config_error
62 65 from IPython.core.application import BaseIPythonApplication
63 66 from IPython.qt.console.ipython_widget import IPythonWidget
64 67 from IPython.qt.console.rich_ipython_widget import RichIPythonWidget
65 68 from IPython.qt.console import styles
66 69 from IPython.qt.console.mainwindow import MainWindow
67 70 from IPython.qt.client import QtKernelClient
68 71 from IPython.qt.manager import QtKernelManager
69 72 from IPython.utils.traitlets import (
70 73 Dict, Unicode, CBool, Any
71 74 )
72 75
73 76 from IPython.consoleapp import (
74 77 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
75 78 )
76 79
77 80 #-----------------------------------------------------------------------------
78 81 # Network Constants
79 82 #-----------------------------------------------------------------------------
80 83
81 84 from IPython.utils.localinterfaces import is_local_ip
82 85
83 86 #-----------------------------------------------------------------------------
84 87 # Globals
85 88 #-----------------------------------------------------------------------------
86 89
87 90 _examples = """
88 91 ipython qtconsole # start the qtconsole
89 92 ipython qtconsole --matplotlib=inline # start with matplotlib inline plotting mode
90 93 """
91 94
92 95 #-----------------------------------------------------------------------------
93 96 # Aliases and Flags
94 97 #-----------------------------------------------------------------------------
95 98
96 99 # start with copy of flags
97 100 flags = dict(flags)
98 101 qt_flags = {
99 102 'plain' : ({'IPythonQtConsoleApp' : {'plain' : True}},
100 103 "Disable rich text support."),
101 104 }
102 105
103 106 # and app_flags from the Console Mixin
104 107 qt_flags.update(app_flags)
105 108 # add frontend flags to the full set
106 109 flags.update(qt_flags)
107 110
108 111 # start with copy of front&backend aliases list
109 112 aliases = dict(aliases)
110 113 qt_aliases = dict(
111 114 style = 'IPythonWidget.syntax_style',
112 115 stylesheet = 'IPythonQtConsoleApp.stylesheet',
113 116 colors = 'ZMQInteractiveShell.colors',
114 117
115 118 editor = 'IPythonWidget.editor',
116 119 paging = 'ConsoleWidget.paging',
117 120 )
118 121 # and app_aliases from the Console Mixin
119 122 qt_aliases.update(app_aliases)
120 123 qt_aliases.update({'gui-completion':'ConsoleWidget.gui_completion'})
121 124 # add frontend aliases to the full set
122 125 aliases.update(qt_aliases)
123 126
124 127 # get flags&aliases into sets, and remove a couple that
125 128 # shouldn't be scrubbed from backend flags:
126 129 qt_aliases = set(qt_aliases.keys())
127 130 qt_aliases.remove('colors')
128 131 qt_flags = set(qt_flags.keys())
129 132
130 133 #-----------------------------------------------------------------------------
131 134 # Classes
132 135 #-----------------------------------------------------------------------------
133 136
134 137 #-----------------------------------------------------------------------------
135 138 # IPythonQtConsole
136 139 #-----------------------------------------------------------------------------
137 140
138 141
139 142 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
140 143 name = 'ipython-qtconsole'
141 144
142 145 description = """
143 146 The IPython QtConsole.
144 147
145 148 This launches a Console-style application using Qt. It is not a full
146 149 console, in that launched terminal subprocesses will not be able to accept
147 150 input.
148 151
149 152 The QtConsole supports various extra features beyond the Terminal IPython
150 153 shell, such as inline plotting with matplotlib, via:
151 154
152 155 ipython qtconsole --matplotlib=inline
153 156
154 157 as well as saving your session as HTML, and printing the output.
155 158
156 159 """
157 160 examples = _examples
158 161
159 162 classes = [IPythonWidget] + IPythonConsoleApp.classes
160 163 flags = Dict(flags)
161 164 aliases = Dict(aliases)
162 165 frontend_flags = Any(qt_flags)
163 166 frontend_aliases = Any(qt_aliases)
164 167 kernel_client_class = QtKernelClient
165 168 kernel_manager_class = QtKernelManager
166 169
167 170 stylesheet = Unicode('', config=True,
168 171 help="path to a custom CSS stylesheet")
169 172
170 173 hide_menubar = CBool(False, config=True,
171 174 help="Start the console window with the menu bar hidden.")
172 175
173 176 maximize = CBool(False, config=True,
174 177 help="Start the console window maximized.")
175 178
176 179 plain = CBool(False, config=True,
177 180 help="Use a plaintext widget instead of rich text (plain can't print/save).")
178 181
179 182 def _plain_changed(self, name, old, new):
180 183 kind = 'plain' if new else 'rich'
181 184 self.config.ConsoleWidget.kind = kind
182 185 if new:
183 186 self.widget_factory = IPythonWidget
184 187 else:
185 188 self.widget_factory = RichIPythonWidget
186 189
187 190 # the factory for creating a widget
188 191 widget_factory = Any(RichIPythonWidget)
189 192
190 193 def parse_command_line(self, argv=None):
191 194 super(IPythonQtConsoleApp, self).parse_command_line(argv)
192 195 self.build_kernel_argv(argv)
193 196
194 197
195 198 def new_frontend_master(self):
196 199 """ Create and return new frontend attached to new kernel, launched on localhost.
197 200 """
198 201 kernel_manager = self.kernel_manager_class(
199 202 connection_file=self._new_connection_file(),
200 203 parent=self,
201 204 autorestart=True,
202 205 )
203 206 # start the kernel
204 207 kwargs = dict()
205 208 kwargs['extra_arguments'] = self.kernel_argv
206 209 kernel_manager.start_kernel(**kwargs)
207 210 kernel_manager.client_factory = self.kernel_client_class
208 211 kernel_client = kernel_manager.client()
209 212 kernel_client.start_channels(shell=True, iopub=True)
210 213 widget = self.widget_factory(config=self.config,
211 214 local_kernel=True)
212 215 self.init_colors(widget)
213 216 widget.kernel_manager = kernel_manager
214 217 widget.kernel_client = kernel_client
215 218 widget._existing = False
216 219 widget._may_close = True
217 220 widget._confirm_exit = self.confirm_exit
218 221 return widget
219 222
220 223 def new_frontend_slave(self, current_widget):
221 224 """Create and return a new frontend attached to an existing kernel.
222 225
223 226 Parameters
224 227 ----------
225 228 current_widget : IPythonWidget
226 229 The IPythonWidget whose kernel this frontend is to share
227 230 """
228 231 kernel_client = self.kernel_client_class(
229 232 connection_file=current_widget.kernel_client.connection_file,
230 233 config = self.config,
231 234 )
232 235 kernel_client.load_connection_file()
233 236 kernel_client.start_channels()
234 237 widget = self.widget_factory(config=self.config,
235 238 local_kernel=False)
236 239 self.init_colors(widget)
237 240 widget._existing = True
238 241 widget._may_close = False
239 242 widget._confirm_exit = False
240 243 widget.kernel_client = kernel_client
241 244 widget.kernel_manager = current_widget.kernel_manager
242 245 return widget
243 246
244 247 def init_qt_app(self):
245 248 # separate from qt_elements, because it must run first
246 249 self.app = QtGui.QApplication([])
247 250
248 251 def init_qt_elements(self):
249 252 # Create the widget.
250 253
251 254 base_path = os.path.abspath(os.path.dirname(__file__))
252 255 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
253 256 self.app.icon = QtGui.QIcon(icon_path)
254 257 QtGui.QApplication.setWindowIcon(self.app.icon)
255 258
256 259 ip = self.ip
257 260 local_kernel = (not self.existing) or is_local_ip(ip)
258 261 self.widget = self.widget_factory(config=self.config,
259 262 local_kernel=local_kernel)
260 263 self.init_colors(self.widget)
261 264 self.widget._existing = self.existing
262 265 self.widget._may_close = not self.existing
263 266 self.widget._confirm_exit = self.confirm_exit
264 267
265 268 self.widget.kernel_manager = self.kernel_manager
266 269 self.widget.kernel_client = self.kernel_client
267 270 self.window = MainWindow(self.app,
268 271 confirm_exit=self.confirm_exit,
269 272 new_frontend_factory=self.new_frontend_master,
270 273 slave_frontend_factory=self.new_frontend_slave,
271 274 )
272 275 self.window.log = self.log
273 276 self.window.add_tab_with_frontend(self.widget)
274 277 self.window.init_magic_helper()
275 278 self.window.init_menu_bar()
276 279
277 280 # Ignore on OSX, where there is always a menu bar
278 281 if sys.platform != 'darwin' and self.hide_menubar:
279 282 self.window.menuBar().setVisible(False)
280 283
281 284 self.window.setWindowTitle('IPython')
282 285
283 286 def init_colors(self, widget):
284 287 """Configure the coloring of the widget"""
285 288 # Note: This will be dramatically simplified when colors
286 289 # are removed from the backend.
287 290
288 291 # parse the colors arg down to current known labels
289 292 cfg = self.config
290 293 colors = cfg.ZMQInteractiveShell.colors if 'ZMQInteractiveShell.colors' in cfg else None
291 294 style = cfg.IPythonWidget.syntax_style if 'IPythonWidget.syntax_style' in cfg else None
292 295 sheet = cfg.IPythonWidget.style_sheet if 'IPythonWidget.style_sheet' in cfg else None
293 296
294 297 # find the value for colors:
295 298 if colors:
296 299 colors=colors.lower()
297 300 if colors in ('lightbg', 'light'):
298 301 colors='lightbg'
299 302 elif colors in ('dark', 'linux'):
300 303 colors='linux'
301 304 else:
302 305 colors='nocolor'
303 306 elif style:
304 307 if style=='bw':
305 308 colors='nocolor'
306 309 elif styles.dark_style(style):
307 310 colors='linux'
308 311 else:
309 312 colors='lightbg'
310 313 else:
311 314 colors=None
312 315
313 316 # Configure the style
314 317 if style:
315 318 widget.style_sheet = styles.sheet_from_template(style, colors)
316 319 widget.syntax_style = style
317 320 widget._syntax_style_changed()
318 321 widget._style_sheet_changed()
319 322 elif colors:
320 323 # use a default dark/light/bw style
321 324 widget.set_default_style(colors=colors)
322 325
323 326 if self.stylesheet:
324 327 # we got an explicit stylesheet
325 328 if os.path.isfile(self.stylesheet):
326 329 with open(self.stylesheet) as f:
327 330 sheet = f.read()
328 331 else:
329 332 raise IOError("Stylesheet %r not found." % self.stylesheet)
330 333 if sheet:
331 334 widget.style_sheet = sheet
332 335 widget._style_sheet_changed()
333 336
334 337
335 338 def init_signal(self):
336 339 """allow clean shutdown on sigint"""
337 340 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
338 341 # need a timer, so that QApplication doesn't block until a real
339 342 # Qt event fires (can require mouse movement)
340 343 # timer trick from http://stackoverflow.com/q/4938723/938949
341 344 timer = QtCore.QTimer()
342 345 # Let the interpreter run each 200 ms:
343 346 timer.timeout.connect(lambda: None)
344 347 timer.start(200)
345 348 # hold onto ref, so the timer doesn't get cleaned up
346 349 self._sigint_timer = timer
347 350
348 351 @catch_config_error
349 352 def initialize(self, argv=None):
350 353 self.init_qt_app()
351 354 super(IPythonQtConsoleApp, self).initialize(argv)
352 355 IPythonConsoleApp.initialize(self,argv)
353 356 self.init_qt_elements()
354 357 self.init_signal()
355 358
356 359 def start(self):
357 360
358 361 # draw the window
359 362 if self.maximize:
360 363 self.window.showMaximized()
361 364 else:
362 365 self.window.show()
363 366 self.window.raise_()
364 367
365 368 # Start the application main loop.
366 369 self.app.exec_()
367 370
368 371 #-----------------------------------------------------------------------------
369 372 # Main entry point
370 373 #-----------------------------------------------------------------------------
371 374
372 375 def main():
373 376 app = IPythonQtConsoleApp()
374 377 app.initialize()
375 378 app.start()
376 379
377 380
378 381 if __name__ == '__main__':
379 382 main()
General Comments 0
You need to be logged in to leave comments. Login now