##// END OF EJS Templates
adjust qtconsole color logic...
MinRK -
Show More
@@ -1,353 +1,356
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 = self.kernel_manager_class(
181 kernel_manager = self.kernel_manager_class(
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 self.init_colors(widget)
193 widget.kernel_manager = kernel_manager
194 widget.kernel_manager = kernel_manager
194 widget._existing = False
195 widget._existing = False
195 widget._may_close = True
196 widget._may_close = True
196 widget._confirm_exit = self.confirm_exit
197 widget._confirm_exit = self.confirm_exit
197 return widget
198 return widget
198
199
199 def new_frontend_slave(self, current_widget):
200 def new_frontend_slave(self, current_widget):
200 """Create and return a new frontend attached to an existing kernel.
201 """Create and return a new frontend attached to an existing kernel.
201
202
202 Parameters
203 Parameters
203 ----------
204 ----------
204 current_widget : IPythonWidget
205 current_widget : IPythonWidget
205 The IPythonWidget whose kernel this frontend is to share
206 The IPythonWidget whose kernel this frontend is to share
206 """
207 """
207 kernel_manager = self.kernel_manager_class(
208 kernel_manager = self.kernel_manager_class(
208 connection_file=current_widget.kernel_manager.connection_file,
209 connection_file=current_widget.kernel_manager.connection_file,
209 config = self.config,
210 config = self.config,
210 )
211 )
211 kernel_manager.load_connection_file()
212 kernel_manager.load_connection_file()
212 kernel_manager.start_channels()
213 kernel_manager.start_channels()
213 widget = self.widget_factory(config=self.config,
214 widget = self.widget_factory(config=self.config,
214 local_kernel=False)
215 local_kernel=False)
216 self.init_colors(widget)
215 widget._existing = True
217 widget._existing = True
216 widget._may_close = False
218 widget._may_close = False
217 widget._confirm_exit = False
219 widget._confirm_exit = False
218 widget.kernel_manager = kernel_manager
220 widget.kernel_manager = kernel_manager
219 return widget
221 return widget
220
222
221 def init_qt_elements(self):
223 def init_qt_elements(self):
222 # Create the widget.
224 # Create the widget.
223 self.app = QtGui.QApplication([])
225 self.app = QtGui.QApplication([])
224
226
225 base_path = os.path.abspath(os.path.dirname(__file__))
227 base_path = os.path.abspath(os.path.dirname(__file__))
226 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
228 icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg')
227 self.app.icon = QtGui.QIcon(icon_path)
229 self.app.icon = QtGui.QIcon(icon_path)
228 QtGui.QApplication.setWindowIcon(self.app.icon)
230 QtGui.QApplication.setWindowIcon(self.app.icon)
229
231
230 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
232 local_kernel = (not self.existing) or self.ip in LOCAL_IPS
231 self.widget = self.widget_factory(config=self.config,
233 self.widget = self.widget_factory(config=self.config,
232 local_kernel=local_kernel)
234 local_kernel=local_kernel)
235 self.init_colors(self.widget)
233 self.widget._existing = self.existing
236 self.widget._existing = self.existing
234 self.widget._may_close = not self.existing
237 self.widget._may_close = not self.existing
235 self.widget._confirm_exit = self.confirm_exit
238 self.widget._confirm_exit = self.confirm_exit
236
239
237 self.widget.kernel_manager = self.kernel_manager
240 self.widget.kernel_manager = self.kernel_manager
238 self.window = MainWindow(self.app,
241 self.window = MainWindow(self.app,
239 confirm_exit=self.confirm_exit,
242 confirm_exit=self.confirm_exit,
240 new_frontend_factory=self.new_frontend_master,
243 new_frontend_factory=self.new_frontend_master,
241 slave_frontend_factory=self.new_frontend_slave,
244 slave_frontend_factory=self.new_frontend_slave,
242 )
245 )
243 self.window.log = self.log
246 self.window.log = self.log
244 self.window.add_tab_with_frontend(self.widget)
247 self.window.add_tab_with_frontend(self.widget)
245 self.window.init_menu_bar()
248 self.window.init_menu_bar()
246
249
247 self.window.setWindowTitle('Python' if self.pure else 'IPython')
250 self.window.setWindowTitle('Python' if self.pure else 'IPython')
248
251
249 def init_colors(self):
252 def init_colors(self, widget):
250 """Configure the coloring of the widget"""
253 """Configure the coloring of the widget"""
251 # Note: This will be dramatically simplified when colors
254 # Note: This will be dramatically simplified when colors
252 # are removed from the backend.
255 # are removed from the backend.
253
256
254 if self.pure:
257 if self.pure:
255 # only IPythonWidget supports styling
258 # only IPythonWidget supports styling
256 return
259 return
257
260
258 # parse the colors arg down to current known labels
261 # parse the colors arg down to current known labels
259 try:
262 try:
260 colors = self.config.ZMQInteractiveShell.colors
263 colors = self.config.ZMQInteractiveShell.colors
261 except AttributeError:
264 except AttributeError:
262 colors = None
265 colors = None
263 try:
266 try:
264 style = self.config.IPythonWidget.syntax_style
267 style = self.config.IPythonWidget.syntax_style
265 except AttributeError:
268 except AttributeError:
266 style = None
269 style = None
270 try:
271 sheet = self.config.IPythonWidget.style_sheet
272 except AttributeError:
273 sheet = None
267
274
268 # find the value for colors:
275 # find the value for colors:
269 if colors:
276 if colors:
270 colors=colors.lower()
277 colors=colors.lower()
271 if colors in ('lightbg', 'light'):
278 if colors in ('lightbg', 'light'):
272 colors='lightbg'
279 colors='lightbg'
273 elif colors in ('dark', 'linux'):
280 elif colors in ('dark', 'linux'):
274 colors='linux'
281 colors='linux'
275 else:
282 else:
276 colors='nocolor'
283 colors='nocolor'
277 elif style:
284 elif style:
278 if style=='bw':
285 if style=='bw':
279 colors='nocolor'
286 colors='nocolor'
280 elif styles.dark_style(style):
287 elif styles.dark_style(style):
281 colors='linux'
288 colors='linux'
282 else:
289 else:
283 colors='lightbg'
290 colors='lightbg'
284 else:
291 else:
285 colors=None
292 colors=None
286
293
287 # Configure the style.
294 # Configure the style
288 widget = self.widget
289 if style:
295 if style:
290 widget.style_sheet = styles.sheet_from_template(style, colors)
296 widget.style_sheet = styles.sheet_from_template(style, colors)
291 widget.syntax_style = style
297 widget.syntax_style = style
292 widget._syntax_style_changed()
298 widget._syntax_style_changed()
293 widget._style_sheet_changed()
299 widget._style_sheet_changed()
294 elif colors:
300 elif colors:
295 # use a default style
301 # use a default dark/light/bw style
296 widget.set_default_style(colors=colors)
302 widget.set_default_style(colors=colors)
297 else:
298 # this is redundant for now, but allows the widget's
299 # defaults to change
300 widget.set_default_style()
301
303
302 if self.stylesheet:
304 if self.stylesheet:
303 # we got an expicit stylesheet
305 # we got an explicit stylesheet
304 if os.path.isfile(self.stylesheet):
306 if os.path.isfile(self.stylesheet):
305 with open(self.stylesheet) as f:
307 with open(self.stylesheet) as f:
306 sheet = f.read()
308 sheet = f.read()
307 widget.style_sheet = sheet
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 if sheet:
312 widget.style_sheet = sheet
313 widget._style_sheet_changed()
314
311
315
312 def init_signal(self):
316 def init_signal(self):
313 """allow clean shutdown on sigint"""
317 """allow clean shutdown on sigint"""
314 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
318 signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2))
315 # need a timer, so that QApplication doesn't block until a real
319 # need a timer, so that QApplication doesn't block until a real
316 # Qt event fires (can require mouse movement)
320 # Qt event fires (can require mouse movement)
317 # timer trick from http://stackoverflow.com/q/4938723/938949
321 # timer trick from http://stackoverflow.com/q/4938723/938949
318 timer = QtCore.QTimer()
322 timer = QtCore.QTimer()
319 # Let the interpreter run each 200 ms:
323 # Let the interpreter run each 200 ms:
320 timer.timeout.connect(lambda: None)
324 timer.timeout.connect(lambda: None)
321 timer.start(200)
325 timer.start(200)
322 # hold onto ref, so the timer doesn't get cleaned up
326 # hold onto ref, so the timer doesn't get cleaned up
323 self._sigint_timer = timer
327 self._sigint_timer = timer
324
328
325 @catch_config_error
329 @catch_config_error
326 def initialize(self, argv=None):
330 def initialize(self, argv=None):
327 super(IPythonQtConsoleApp, self).initialize(argv)
331 super(IPythonQtConsoleApp, self).initialize(argv)
328 IPythonConsoleApp.initialize(self,argv)
332 IPythonConsoleApp.initialize(self,argv)
329 self.init_qt_elements()
333 self.init_qt_elements()
330 self.init_colors()
331 self.init_signal()
334 self.init_signal()
332
335
333 def start(self):
336 def start(self):
334
337
335 # draw the window
338 # draw the window
336 self.window.show()
339 self.window.show()
337 self.window.raise_()
340 self.window.raise_()
338
341
339 # Start the application main loop.
342 # Start the application main loop.
340 self.app.exec_()
343 self.app.exec_()
341
344
342 #-----------------------------------------------------------------------------
345 #-----------------------------------------------------------------------------
343 # Main entry point
346 # Main entry point
344 #-----------------------------------------------------------------------------
347 #-----------------------------------------------------------------------------
345
348
346 def main():
349 def main():
347 app = IPythonQtConsoleApp()
350 app = IPythonQtConsoleApp()
348 app.initialize()
351 app.initialize()
349 app.start()
352 app.start()
350
353
351
354
352 if __name__ == '__main__':
355 if __name__ == '__main__':
353 main()
356 main()
General Comments 0
You need to be logged in to leave comments. Login now