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