##// END OF EJS Templates
put qtconsole forground when launching
Matthias BUSSONNIER -
Show More
@@ -1,352 +1,353 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 * Bussonnier Matthias
12 * Bussonnier Matthias
13 * Thomas Kluyver
13 * Thomas Kluyver
14 * Paul Ivanov
14 * Paul Ivanov
15
15
16 """
16 """
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 # stdlib imports
22 # stdlib imports
23 import json
23 import json
24 import os
24 import os
25 import signal
25 import signal
26 import sys
26 import sys
27 import uuid
27 import uuid
28
28
29 # System library imports
29 # System library imports
30 from IPython.external.qt import QtCore, QtGui
30 from IPython.external.qt import QtCore, QtGui
31
31
32 # Local imports
32 # Local imports
33 from IPython.config.application import boolean_flag, catch_config_error
33 from IPython.config.application import boolean_flag, catch_config_error
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.lib.kernel import tunnel_to_kernel, find_connection_file
36 from IPython.lib.kernel import tunnel_to_kernel, find_connection_file
37 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
37 from IPython.frontend.qt.console.frontend_widget import FrontendWidget
38 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
38 from IPython.frontend.qt.console.ipython_widget import IPythonWidget
39 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
39 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
40 from IPython.frontend.qt.console import styles
40 from IPython.frontend.qt.console import styles
41 from IPython.frontend.qt.console.mainwindow import MainWindow
41 from IPython.frontend.qt.console.mainwindow import MainWindow
42 from IPython.frontend.qt.kernelmanager import QtKernelManager
42 from IPython.frontend.qt.kernelmanager import QtKernelManager
43 from IPython.utils.path import filefind
43 from IPython.utils.path import filefind
44 from IPython.utils.py3compat import str_to_bytes
44 from IPython.utils.py3compat import str_to_bytes
45 from IPython.utils.traitlets import (
45 from IPython.utils.traitlets import (
46 Dict, List, Unicode, Integer, CaselessStrEnum, CBool, Any
46 Dict, List, Unicode, Integer, CaselessStrEnum, CBool, Any
47 )
47 )
48 from IPython.zmq.ipkernel import IPKernelApp
48 from IPython.zmq.ipkernel import IPKernelApp
49 from IPython.zmq.session import Session, default_secure
49 from IPython.zmq.session import Session, default_secure
50 from IPython.zmq.zmqshell import ZMQInteractiveShell
50 from IPython.zmq.zmqshell import ZMQInteractiveShell
51
51
52 from IPython.frontend.consoleapp import (
52 from IPython.frontend.consoleapp import (
53 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
53 IPythonConsoleApp, app_aliases, app_flags, flags, aliases
54 )
54 )
55
55
56 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
57 # Network Constants
57 # Network Constants
58 #-----------------------------------------------------------------------------
58 #-----------------------------------------------------------------------------
59
59
60 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
60 from IPython.utils.localinterfaces import LOCALHOST, LOCAL_IPS
61
61
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63 # Globals
63 # Globals
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65
65
66 _examples = """
66 _examples = """
67 ipython qtconsole # start the qtconsole
67 ipython qtconsole # start the qtconsole
68 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
68 ipython qtconsole --pylab=inline # start with pylab in inline plotting mode
69 """
69 """
70
70
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72 # Aliases and Flags
72 # Aliases and Flags
73 #-----------------------------------------------------------------------------
73 #-----------------------------------------------------------------------------
74
74
75 # start with copy of flags
75 # start with copy of flags
76 flags = dict(flags)
76 flags = dict(flags)
77 qt_flags = {
77 qt_flags = {
78 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
78 'pure' : ({'IPythonQtConsoleApp' : {'pure' : True}},
79 "Use a pure Python kernel instead of an IPython kernel."),
79 "Use a pure Python kernel instead of an IPython kernel."),
80 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
80 'plain' : ({'ConsoleWidget' : {'kind' : 'plain'}},
81 "Disable rich text support."),
81 "Disable rich text support."),
82 }
82 }
83 qt_flags.update(boolean_flag(
83 qt_flags.update(boolean_flag(
84 'gui-completion', 'ConsoleWidget.gui_completion',
84 'gui-completion', 'ConsoleWidget.gui_completion',
85 "use a GUI widget for tab completion",
85 "use a GUI widget for tab completion",
86 "use plaintext output for completion"
86 "use plaintext output for completion"
87 ))
87 ))
88 # and app_flags from the Console Mixin
88 # and app_flags from the Console Mixin
89 qt_flags.update(app_flags)
89 qt_flags.update(app_flags)
90 # add frontend flags to the full set
90 # add frontend flags to the full set
91 flags.update(qt_flags)
91 flags.update(qt_flags)
92
92
93 # start with copy of front&backend aliases list
93 # start with copy of front&backend aliases list
94 aliases = dict(aliases)
94 aliases = dict(aliases)
95 qt_aliases = dict(
95 qt_aliases = dict(
96
96
97 style = 'IPythonWidget.syntax_style',
97 style = 'IPythonWidget.syntax_style',
98 stylesheet = 'IPythonQtConsoleApp.stylesheet',
98 stylesheet = 'IPythonQtConsoleApp.stylesheet',
99 colors = 'ZMQInteractiveShell.colors',
99 colors = 'ZMQInteractiveShell.colors',
100
100
101 editor = 'IPythonWidget.editor',
101 editor = 'IPythonWidget.editor',
102 paging = 'ConsoleWidget.paging',
102 paging = 'ConsoleWidget.paging',
103 )
103 )
104 # and app_aliases from the Console Mixin
104 # and app_aliases from the Console Mixin
105 qt_aliases.update(app_aliases)
105 qt_aliases.update(app_aliases)
106 # add frontend aliases to the full set
106 # add frontend aliases to the full set
107 aliases.update(qt_aliases)
107 aliases.update(qt_aliases)
108
108
109 # get flags&aliases into sets, and remove a couple that
109 # get flags&aliases into sets, and remove a couple that
110 # shouldn't be scrubbed from backend flags:
110 # shouldn't be scrubbed from backend flags:
111 qt_aliases = set(qt_aliases.keys())
111 qt_aliases = set(qt_aliases.keys())
112 qt_aliases.remove('colors')
112 qt_aliases.remove('colors')
113 qt_flags = set(qt_flags.keys())
113 qt_flags = set(qt_flags.keys())
114
114
115 #-----------------------------------------------------------------------------
115 #-----------------------------------------------------------------------------
116 # Classes
116 # Classes
117 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
118
118
119 #-----------------------------------------------------------------------------
119 #-----------------------------------------------------------------------------
120 # IPythonQtConsole
120 # IPythonQtConsole
121 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
122
122
123
123
124 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
124 class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp):
125 name = 'ipython-qtconsole'
125 name = 'ipython-qtconsole'
126
126
127 description = """
127 description = """
128 The IPython QtConsole.
128 The IPython QtConsole.
129
129
130 This launches a Console-style application using Qt. It is not a full
130 This launches a Console-style application using Qt. It is not a full
131 console, in that launched terminal subprocesses will not be able to accept
131 console, in that launched terminal subprocesses will not be able to accept
132 input.
132 input.
133
133
134 The QtConsole supports various extra features beyond the Terminal IPython
134 The QtConsole supports various extra features beyond the Terminal IPython
135 shell, such as inline plotting with matplotlib, via:
135 shell, such as inline plotting with matplotlib, via:
136
136
137 ipython qtconsole --pylab=inline
137 ipython qtconsole --pylab=inline
138
138
139 as well as saving your session as HTML, and printing the output.
139 as well as saving your session as HTML, and printing the output.
140
140
141 """
141 """
142 examples = _examples
142 examples = _examples
143
143
144 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
144 classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session]
145 flags = Dict(flags)
145 flags = Dict(flags)
146 aliases = Dict(aliases)
146 aliases = Dict(aliases)
147 frontend_flags = Any(qt_flags)
147 frontend_flags = Any(qt_flags)
148 frontend_aliases = Any(qt_aliases)
148 frontend_aliases = Any(qt_aliases)
149 kernel_manager_class = QtKernelManager
149 kernel_manager_class = QtKernelManager
150
150
151 stylesheet = Unicode('', config=True,
151 stylesheet = Unicode('', config=True,
152 help="path to a custom CSS stylesheet")
152 help="path to a custom CSS stylesheet")
153
153
154 plain = CBool(False, config=True,
154 plain = CBool(False, config=True,
155 help="Use a plaintext widget instead of rich text (plain can't print/save).")
155 help="Use a plaintext widget instead of rich text (plain can't print/save).")
156
156
157 def _pure_changed(self, name, old, new):
157 def _pure_changed(self, name, old, new):
158 kind = 'plain' if self.plain else 'rich'
158 kind = 'plain' if self.plain else 'rich'
159 self.config.ConsoleWidget.kind = kind
159 self.config.ConsoleWidget.kind = kind
160 if self.pure:
160 if self.pure:
161 self.widget_factory = FrontendWidget
161 self.widget_factory = FrontendWidget
162 elif self.plain:
162 elif self.plain:
163 self.widget_factory = IPythonWidget
163 self.widget_factory = IPythonWidget
164 else:
164 else:
165 self.widget_factory = RichIPythonWidget
165 self.widget_factory = RichIPythonWidget
166
166
167 _plain_changed = _pure_changed
167 _plain_changed = _pure_changed
168
168
169 # the factory for creating a widget
169 # the factory for creating a widget
170 widget_factory = Any(RichIPythonWidget)
170 widget_factory = Any(RichIPythonWidget)
171
171
172 def parse_command_line(self, argv=None):
172 def parse_command_line(self, argv=None):
173 super(IPythonQtConsoleApp, self).parse_command_line(argv)
173 super(IPythonQtConsoleApp, self).parse_command_line(argv)
174 self.build_kernel_argv(argv)
174 self.build_kernel_argv(argv)
175
175
176
176
177 def new_frontend_master(self):
177 def new_frontend_master(self):
178 """ Create and return new frontend attached to new kernel, launched on localhost.
178 """ Create and return new frontend attached to new kernel, launched on localhost.
179 """
179 """
180 ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST
180 ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST
181 kernel_manager = QtKernelManager(
181 kernel_manager = QtKernelManager(
182 ip=ip,
182 ip=ip,
183 connection_file=self._new_connection_file(),
183 connection_file=self._new_connection_file(),
184 config=self.config,
184 config=self.config,
185 )
185 )
186 # start the kernel
186 # start the kernel
187 kwargs = dict(ipython=not self.pure)
187 kwargs = dict(ipython=not self.pure)
188 kwargs['extra_arguments'] = self.kernel_argv
188 kwargs['extra_arguments'] = self.kernel_argv
189 kernel_manager.start_kernel(**kwargs)
189 kernel_manager.start_kernel(**kwargs)
190 kernel_manager.start_channels()
190 kernel_manager.start_channels()
191 widget = self.widget_factory(config=self.config,
191 widget = self.widget_factory(config=self.config,
192 local_kernel=True)
192 local_kernel=True)
193 widget.kernel_manager = kernel_manager
193 widget.kernel_manager = kernel_manager
194 widget._existing = False
194 widget._existing = False
195 widget._may_close = True
195 widget._may_close = True
196 widget._confirm_exit = self.confirm_exit
196 widget._confirm_exit = self.confirm_exit
197 return widget
197 return widget
198
198
199 def new_frontend_slave(self, current_widget):
199 def new_frontend_slave(self, current_widget):
200 """Create and return a new frontend attached to an existing kernel.
200 """Create and return a new frontend attached to an existing kernel.
201
201
202 Parameters
202 Parameters
203 ----------
203 ----------
204 current_widget : IPythonWidget
204 current_widget : IPythonWidget
205 The IPythonWidget whose kernel this frontend is to share
205 The IPythonWidget whose kernel this frontend is to share
206 """
206 """
207 kernel_manager = QtKernelManager(
207 kernel_manager = QtKernelManager(
208 connection_file=current_widget.kernel_manager.connection_file,
208 connection_file=current_widget.kernel_manager.connection_file,
209 config = self.config,
209 config = self.config,
210 )
210 )
211 kernel_manager.load_connection_file()
211 kernel_manager.load_connection_file()
212 kernel_manager.start_channels()
212 kernel_manager.start_channels()
213 widget = self.widget_factory(config=self.config,
213 widget = self.widget_factory(config=self.config,
214 local_kernel=False)
214 local_kernel=False)
215 widget._existing = True
215 widget._existing = True
216 widget._may_close = False
216 widget._may_close = False
217 widget._confirm_exit = False
217 widget._confirm_exit = False
218 widget.kernel_manager = kernel_manager
218 widget.kernel_manager = kernel_manager
219 return widget
219 return widget
220
220
221 def init_qt_elements(self):
221 def init_qt_elements(self):
222 # Create the widget.
222 # Create the widget.
223 self.app = QtGui.QApplication([])
223 self.app = QtGui.QApplication([])
224
224
225 base_path = os.path.abspath(os.path.dirname(__file__))
225 base_path = os.path.abspath(os.path.dirname(__file__))
226 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
226 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
227 self.app.icon = QtGui.QIcon(icon_path)
227 self.app.icon = QtGui.QIcon(icon_path)
228 QtGui.QApplication.setWindowIcon(self.app.icon)
228 QtGui.QApplication.setWindowIcon(self.app.icon)
229
229
230 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
230 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
231 self.widget = self.widget_factory(config=self.config,
231 self.widget = self.widget_factory(config=self.config,
232 local_kernel=local_kernel)
232 local_kernel=local_kernel)
233 self.widget._existing = self.existing
233 self.widget._existing = self.existing
234 self.widget._may_close = not self.existing
234 self.widget._may_close = not self.existing
235 self.widget._confirm_exit = self.confirm_exit
235 self.widget._confirm_exit = self.confirm_exit
236
236
237 self.widget.kernel_manager = self.kernel_manager
237 self.widget.kernel_manager = self.kernel_manager
238 self.window = MainWindow(self.app,
238 self.window = MainWindow(self.app,
239 confirm_exit=self.confirm_exit,
239 confirm_exit=self.confirm_exit,
240 new_frontend_factory=self.new_frontend_master,
240 new_frontend_factory=self.new_frontend_master,
241 slave_frontend_factory=self.new_frontend_slave,
241 slave_frontend_factory=self.new_frontend_slave,
242 )
242 )
243 self.window.log = self.log
243 self.window.log = self.log
244 self.window.add_tab_with_frontend(self.widget)
244 self.window.add_tab_with_frontend(self.widget)
245 self.window.init_menu_bar()
245 self.window.init_menu_bar()
246
246
247 self.window.setWindowTitle('Python' if self.pure else 'IPython')
247 self.window.setWindowTitle('Python' if self.pure else 'IPython')
248
248
249 def init_colors(self):
249 def init_colors(self):
250 """Configure the coloring of the widget"""
250 """Configure the coloring of the widget"""
251 # Note: This will be dramatically simplified when colors
251 # Note: This will be dramatically simplified when colors
252 # are removed from the backend.
252 # are removed from the backend.
253
253
254 if self.pure:
254 if self.pure:
255 # only IPythonWidget supports styling
255 # only IPythonWidget supports styling
256 return
256 return
257
257
258 # parse the colors arg down to current known labels
258 # parse the colors arg down to current known labels
259 try:
259 try:
260 colors = self.config.ZMQInteractiveShell.colors
260 colors = self.config.ZMQInteractiveShell.colors
261 except AttributeError:
261 except AttributeError:
262 colors = None
262 colors = None
263 try:
263 try:
264 style = self.config.IPythonWidget.syntax_style
264 style = self.config.IPythonWidget.syntax_style
265 except AttributeError:
265 except AttributeError:
266 style = None
266 style = None
267
267
268 # find the value for colors:
268 # find the value for colors:
269 if colors:
269 if colors:
270 colors=colors.lower()
270 colors=colors.lower()
271 if colors in ('lightbg', 'light'):
271 if colors in ('lightbg', 'light'):
272 colors='lightbg'
272 colors='lightbg'
273 elif colors in ('dark', 'linux'):
273 elif colors in ('dark', 'linux'):
274 colors='linux'
274 colors='linux'
275 else:
275 else:
276 colors='nocolor'
276 colors='nocolor'
277 elif style:
277 elif style:
278 if style=='bw':
278 if style=='bw':
279 colors='nocolor'
279 colors='nocolor'
280 elif styles.dark_style(style):
280 elif styles.dark_style(style):
281 colors='linux'
281 colors='linux'
282 else:
282 else:
283 colors='lightbg'
283 colors='lightbg'
284 else:
284 else:
285 colors=None
285 colors=None
286
286
287 # Configure the style.
287 # Configure the style.
288 widget = self.widget
288 widget = self.widget
289 if style:
289 if style:
290 widget.style_sheet = styles.sheet_from_template(style, colors)
290 widget.style_sheet = styles.sheet_from_template(style, colors)
291 widget.syntax_style = style
291 widget.syntax_style = style
292 widget._syntax_style_changed()
292 widget._syntax_style_changed()
293 widget._style_sheet_changed()
293 widget._style_sheet_changed()
294 elif colors:
294 elif colors:
295 # use a default style
295 # use a default style
296 widget.set_default_style(colors=colors)
296 widget.set_default_style(colors=colors)
297 else:
297 else:
298 # this is redundant for now, but allows the widget's
298 # this is redundant for now, but allows the widget's
299 # defaults to change
299 # defaults to change
300 widget.set_default_style()
300 widget.set_default_style()
301
301
302 if self.stylesheet:
302 if self.stylesheet:
303 # we got an expicit stylesheet
303 # we got an expicit stylesheet
304 if os.path.isfile(self.stylesheet):
304 if os.path.isfile(self.stylesheet):
305 with open(self.stylesheet) as f:
305 with open(self.stylesheet) as f:
306 sheet = f.read()
306 sheet = f.read()
307 widget.style_sheet = sheet
307 widget.style_sheet = sheet
308 widget._style_sheet_changed()
308 widget._style_sheet_changed()
309 else:
309 else:
310 raise IOError("Stylesheet %r not found."%self.stylesheet)
310 raise IOError("Stylesheet %r not found."%self.stylesheet)
311
311
312 def init_signal(self):
312 def init_signal(self):
313 """allow clean shutdown on sigint"""
313 """allow clean shutdown on sigint"""
314 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
314 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
315 # need a timer, so that QApplication doesn't block until a real
315 # need a timer, so that QApplication doesn't block until a real
316 # Qt event fires (can require mouse movement)
316 # Qt event fires (can require mouse movement)
317 # timer trick from http://stackoverflow.com/q/4938723/938949
317 # timer trick from http://stackoverflow.com/q/4938723/938949
318 timer = QtCore.QTimer()
318 timer = QtCore.QTimer()
319 # Let the interpreter run each 200 ms:
319 # Let the interpreter run each 200 ms:
320 timer.timeout.connect(lambda: None)
320 timer.timeout.connect(lambda: None)
321 timer.start(200)
321 timer.start(200)
322 # hold onto ref, so the timer doesn't get cleaned up
322 # hold onto ref, so the timer doesn't get cleaned up
323 self._sigint_timer = timer
323 self._sigint_timer = timer
324
324
325 @catch_config_error
325 @catch_config_error
326 def initialize(self, argv=None):
326 def initialize(self, argv=None):
327 super(IPythonQtConsoleApp, self).initialize(argv)
327 super(IPythonQtConsoleApp, self).initialize(argv)
328 IPythonConsoleApp.initialize(self,argv)
328 IPythonConsoleApp.initialize(self,argv)
329 self.init_qt_elements()
329 self.init_qt_elements()
330 self.init_colors()
330 self.init_colors()
331 self.init_signal()
331 self.init_signal()
332
332
333 def start(self):
333 def start(self):
334
334
335 # draw the window
335 # draw the window
336 self.window.show()
336 self.window.show()
337 self.window.raise_()
337
338
338 # Start the application main loop.
339 # Start the application main loop.
339 self.app.exec_()
340 self.app.exec_()
340
341
341 #-----------------------------------------------------------------------------
342 #-----------------------------------------------------------------------------
342 # Main entry point
343 # Main entry point
343 #-----------------------------------------------------------------------------
344 #-----------------------------------------------------------------------------
344
345
345 def main():
346 def main():
346 app = IPythonQtConsoleApp()
347 app = IPythonQtConsoleApp()
347 app.initialize()
348 app.initialize()
348 app.start()
349 app.start()
349
350
350
351
351 if __name__ == '__main__':
352 if __name__ == '__main__':
352 main()
353 main()
General Comments 0
You need to be logged in to leave comments. Login now