##// END OF EJS Templates
Merge branch 'glut-rebased' of git://github.com/fperez/ipython into glut...
Nicolas Rougier -
r4818:89161a5b merge
parent child Browse files
Show More
@@ -0,0 +1,46 b''
1 #!/usr/bin/env python
2 """Simple GLUT example to manually test event loop integration.
3
4 This is meant to run tests manually in ipython as:
5
6 In [5]: %gui glut
7
8 In [6]: %run gui-glut.py
9
10 In [7]: gl.glClearColor(1,1,1,1)
11 """
12
13 #!/usr/bin/env python
14 import sys
15 import OpenGL.GL as gl
16 import OpenGL.GLUT as glut
17
18 def display():
19 gl.glClear (gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
20 glut.glutSwapBuffers()
21
22 def resize(width,height):
23 gl.glViewport(0, 0, width, height+4)
24 gl.glMatrixMode(gl.GL_PROJECTION)
25 gl.glLoadIdentity()
26 gl.glOrtho(0, width, 0, height+4, -1, 1)
27 gl.glMatrixMode(gl.GL_MODELVIEW)
28
29
30 if glut.glutGetWindow() > 0:
31 interactive = True
32 glut.glutInit(sys.argv)
33 glut.glutInitDisplayMode(glut.GLUT_DOUBLE |
34 glut.GLUT_RGBA |
35 glut.GLUT_DEPTH)
36 glut.glutShowWindow()
37 else:
38 glut.glutCreateWindow('gui-glut')
39 interactive = False
40
41 glut.glutDisplayFunc(display)
42 glut.glutReshapeFunc(resize)
43 gl.glClearColor(0,0,0,1)
44
45 if not interactive:
46 glut.glutMainLoop()
@@ -1,393 +1,393 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The :class:`~IPython.core.application.Application` object for the command
5 5 line :command:`ipython` program.
6 6
7 7 Authors
8 8 -------
9 9
10 10 * Brian Granger
11 11 * Fernando Perez
12 12 * Min Ragan-Kelley
13 13 """
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Copyright (C) 2008-2010 The IPython Development Team
17 17 #
18 18 # Distributed under the terms of the BSD License. The full license is in
19 19 # the file COPYING, distributed as part of this software.
20 20 #-----------------------------------------------------------------------------
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Imports
24 24 #-----------------------------------------------------------------------------
25 25
26 26 from __future__ import absolute_import
27 27
28 28 import logging
29 29 import os
30 30 import sys
31 31
32 32 from IPython.config.loader import (
33 33 Config, PyFileConfigLoader
34 34 )
35 35 from IPython.config.application import boolean_flag
36 36 from IPython.core import release
37 37 from IPython.core import usage
38 38 from IPython.core.crashhandler import CrashHandler
39 39 from IPython.core.formatters import PlainTextFormatter
40 40 from IPython.core.application import (
41 41 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
42 42 )
43 43 from IPython.core.shellapp import (
44 44 InteractiveShellApp, shell_flags, shell_aliases
45 45 )
46 46 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
47 47 from IPython.lib import inputhook
48 48 from IPython.utils import warn
49 49 from IPython.utils.path import get_ipython_dir, check_for_old_config
50 50 from IPython.utils.traitlets import (
51 51 Bool, List, Dict, CaselessStrEnum
52 52 )
53 53
54 54 #-----------------------------------------------------------------------------
55 55 # Globals, utilities and helpers
56 56 #-----------------------------------------------------------------------------
57 57
58 58 #: The default config file name for this application.
59 59 default_config_file_name = u'ipython_config.py'
60 60
61 61 _examples = """
62 62 ipython --pylab # start in pylab mode
63 63 ipython --pylab=qt # start in pylab mode with the qt4 backend
64 64 ipython --log-level=DEBUG # set logging to DEBUG
65 65 ipython --profile=foo # start with profile foo
66 66
67 67 ipython qtconsole # start the qtconsole GUI application
68 68 ipython qtconsole -h # show the help string for the qtconsole subcmd
69 69
70 70 ipython profile create foo # create profile foo w/ default config files
71 71 ipython profile -h # show the help string for the profile subcmd
72 72 """
73 73
74 74 #-----------------------------------------------------------------------------
75 75 # Crash handler for this application
76 76 #-----------------------------------------------------------------------------
77 77
78 78 class IPAppCrashHandler(CrashHandler):
79 79 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
80 80
81 81 def __init__(self, app):
82 82 contact_name = release.authors['Fernando'][0]
83 83 contact_email = release.authors['Fernando'][1]
84 84 bug_tracker = 'http://github.com/ipython/ipython/issues'
85 85 super(IPAppCrashHandler,self).__init__(
86 86 app, contact_name, contact_email, bug_tracker
87 87 )
88 88
89 89 def make_report(self,traceback):
90 90 """Return a string containing a crash report."""
91 91
92 92 sec_sep = self.section_sep
93 93 # Start with parent report
94 94 report = [super(IPAppCrashHandler, self).make_report(traceback)]
95 95 # Add interactive-specific info we may have
96 96 rpt_add = report.append
97 97 try:
98 98 rpt_add(sec_sep+"History of session input:")
99 99 for line in self.app.shell.user_ns['_ih']:
100 100 rpt_add(line)
101 101 rpt_add('\n*** Last line of input (may not be in above history):\n')
102 102 rpt_add(self.app.shell._last_input_line+'\n')
103 103 except:
104 104 pass
105 105
106 106 return ''.join(report)
107 107
108 108 #-----------------------------------------------------------------------------
109 109 # Aliases and Flags
110 110 #-----------------------------------------------------------------------------
111 111 flags = dict(base_flags)
112 112 flags.update(shell_flags)
113 113 addflag = lambda *args: flags.update(boolean_flag(*args))
114 114 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
115 115 'Turn on auto editing of files with syntax errors.',
116 116 'Turn off auto editing of files with syntax errors.'
117 117 )
118 118 addflag('banner', 'TerminalIPythonApp.display_banner',
119 119 "Display a banner upon starting IPython.",
120 120 "Don't display a banner upon starting IPython."
121 121 )
122 122 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
123 123 """Set to confirm when you try to exit IPython with an EOF (Control-D
124 124 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
125 125 you can force a direct exit without any confirmation.""",
126 126 "Don't prompt the user when exiting."
127 127 )
128 128 addflag('term-title', 'TerminalInteractiveShell.term_title',
129 129 "Enable auto setting the terminal title.",
130 130 "Disable auto setting the terminal title."
131 131 )
132 132 classic_config = Config()
133 133 classic_config.InteractiveShell.cache_size = 0
134 134 classic_config.PlainTextFormatter.pprint = False
135 135 classic_config.InteractiveShell.prompt_in1 = '>>> '
136 136 classic_config.InteractiveShell.prompt_in2 = '... '
137 137 classic_config.InteractiveShell.prompt_out = ''
138 138 classic_config.InteractiveShell.separate_in = ''
139 139 classic_config.InteractiveShell.separate_out = ''
140 140 classic_config.InteractiveShell.separate_out2 = ''
141 141 classic_config.InteractiveShell.colors = 'NoColor'
142 142 classic_config.InteractiveShell.xmode = 'Plain'
143 143
144 144 flags['classic']=(
145 145 classic_config,
146 146 "Gives IPython a similar feel to the classic Python prompt."
147 147 )
148 148 # # log doesn't make so much sense this way anymore
149 149 # paa('--log','-l',
150 150 # action='store_true', dest='InteractiveShell.logstart',
151 151 # help="Start logging to the default log file (./ipython_log.py).")
152 152 #
153 153 # # quick is harder to implement
154 154 flags['quick']=(
155 155 {'TerminalIPythonApp' : {'quick' : True}},
156 156 "Enable quick startup with no config files."
157 157 )
158 158
159 159 flags['i'] = (
160 160 {'TerminalIPythonApp' : {'force_interact' : True}},
161 161 """If running code from the command line, become interactive afterwards.
162 162 Note: can also be given simply as '-i.'"""
163 163 )
164 164 flags['pylab'] = (
165 165 {'TerminalIPythonApp' : {'pylab' : 'auto'}},
166 166 """Pre-load matplotlib and numpy for interactive use with
167 167 the default matplotlib backend."""
168 168 )
169 169
170 170 aliases = dict(base_aliases)
171 171 aliases.update(shell_aliases)
172 172
173 173 # it's possible we don't want short aliases for *all* of these:
174 174 aliases.update(dict(
175 175 gui='TerminalIPythonApp.gui',
176 176 pylab='TerminalIPythonApp.pylab',
177 177 ))
178 178
179 179 #-----------------------------------------------------------------------------
180 180 # Main classes and functions
181 181 #-----------------------------------------------------------------------------
182 182
183 183 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
184 184 name = u'ipython'
185 185 description = usage.cl_usage
186 186 default_config_file_name = default_config_file_name
187 187 crash_handler_class = IPAppCrashHandler
188 188 examples = _examples
189 189
190 190 flags = Dict(flags)
191 191 aliases = Dict(aliases)
192 192 classes = List()
193 193 def _classes_default(self):
194 194 """This has to be in a method, for TerminalIPythonApp to be available."""
195 195 return [
196 196 InteractiveShellApp, # ShellApp comes before TerminalApp, because
197 197 self.__class__, # it will also affect subclasses (e.g. QtConsole)
198 198 TerminalInteractiveShell,
199 199 ProfileDir,
200 200 PlainTextFormatter,
201 201 ]
202 202
203 203 subcommands = Dict(dict(
204 204 qtconsole=('IPython.frontend.qt.console.qtconsoleapp.IPythonQtConsoleApp',
205 205 """Launch the IPython Qt Console."""
206 206 ),
207 207 notebook=('IPython.frontend.html.notebook.notebookapp.IPythonNotebookApp',
208 208 """Launch the IPython HTML Notebook Server"""
209 209 ),
210 210 profile = ("IPython.core.profileapp.ProfileApp",
211 211 "Create and manage IPython profiles."
212 212 ),
213 213 kernel = ("IPython.zmq.ipkernel.IPKernelApp",
214 214 "Start a kernel without an attached frontend."
215 215 ),
216 216 ))
217 217
218 218 # *do* autocreate requested profile, but don't create the config file.
219 219 auto_create=Bool(True)
220 220 # configurables
221 221 ignore_old_config=Bool(False, config=True,
222 222 help="Suppress warning messages about legacy config files"
223 223 )
224 224 quick = Bool(False, config=True,
225 225 help="""Start IPython quickly by skipping the loading of config files."""
226 226 )
227 227 def _quick_changed(self, name, old, new):
228 228 if new:
229 229 self.load_config_file = lambda *a, **kw: None
230 230 self.ignore_old_config=True
231 231
232 gui = CaselessStrEnum(('qt','wx','gtk', 'pyglet'), config=True,
233 help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'pyglet')."
232 gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet'), config=True,
233 help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet')."
234 234 )
235 235 pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
236 236 config=True,
237 237 help="""Pre-load matplotlib and numpy for interactive use,
238 238 selecting a particular matplotlib backend and loop integration.
239 239 """
240 240 )
241 241 display_banner = Bool(True, config=True,
242 242 help="Whether to display a banner upon starting IPython."
243 243 )
244 244
245 245 # if there is code of files to run from the cmd line, don't interact
246 246 # unless the --i flag (App.force_interact) is true.
247 247 force_interact = Bool(False, config=True,
248 248 help="""If a command or file is given via the command-line,
249 249 e.g. 'ipython foo.py"""
250 250 )
251 251 def _force_interact_changed(self, name, old, new):
252 252 if new:
253 253 self.interact = True
254 254
255 255 def _file_to_run_changed(self, name, old, new):
256 256 if new and not self.force_interact:
257 257 self.interact = False
258 258 _code_to_run_changed = _file_to_run_changed
259 259
260 260 # internal, not-configurable
261 261 interact=Bool(True)
262 262
263 263
264 264 def parse_command_line(self, argv=None):
265 265 """override to allow old '-pylab' flag with deprecation warning"""
266 266
267 267 argv = sys.argv[1:] if argv is None else argv
268 268
269 269 if '-pylab' in argv:
270 270 # deprecated `-pylab` given,
271 271 # warn and transform into current syntax
272 272 argv = argv[:] # copy, don't clobber
273 273 idx = argv.index('-pylab')
274 274 warn.warn("`-pylab` flag has been deprecated.\n"
275 275 " Use `--pylab` instead, or `--pylab=foo` to specify a backend.")
276 276 sub = '--pylab'
277 277 if len(argv) > idx+1:
278 278 # check for gui arg, as in '-pylab qt'
279 279 gui = argv[idx+1]
280 280 if gui in ('wx', 'qt', 'qt4', 'gtk', 'auto'):
281 281 sub = '--pylab='+gui
282 282 argv.pop(idx+1)
283 283 argv[idx] = sub
284 284
285 285 return super(TerminalIPythonApp, self).parse_command_line(argv)
286 286
287 287 def initialize(self, argv=None):
288 288 """Do actions after construct, but before starting the app."""
289 289 super(TerminalIPythonApp, self).initialize(argv)
290 290 if self.subapp is not None:
291 291 # don't bother initializing further, starting subapp
292 292 return
293 293 if not self.ignore_old_config:
294 294 check_for_old_config(self.ipython_dir)
295 295 # print self.extra_args
296 296 if self.extra_args:
297 297 self.file_to_run = self.extra_args[0]
298 298 # create the shell
299 299 self.init_shell()
300 300 # and draw the banner
301 301 self.init_banner()
302 302 # Now a variety of things that happen after the banner is printed.
303 303 self.init_gui_pylab()
304 304 self.init_extensions()
305 305 self.init_code()
306 306
307 307 def init_shell(self):
308 308 """initialize the InteractiveShell instance"""
309 309 # I am a little hesitant to put these into InteractiveShell itself.
310 310 # But that might be the place for them
311 311 sys.path.insert(0, '')
312 312
313 313 # Create an InteractiveShell instance.
314 314 # shell.display_banner should always be False for the terminal
315 315 # based app, because we call shell.show_banner() by hand below
316 316 # so the banner shows *before* all extension loading stuff.
317 317 self.shell = TerminalInteractiveShell.instance(config=self.config,
318 318 display_banner=False, profile_dir=self.profile_dir,
319 319 ipython_dir=self.ipython_dir)
320 320
321 321 def init_banner(self):
322 322 """optionally display the banner"""
323 323 if self.display_banner and self.interact:
324 324 self.shell.show_banner()
325 325 # Make sure there is a space below the banner.
326 326 if self.log_level <= logging.INFO: print
327 327
328 328
329 329 def init_gui_pylab(self):
330 330 """Enable GUI event loop integration, taking pylab into account."""
331 331 gui = self.gui
332 332
333 333 # Using `pylab` will also require gui activation, though which toolkit
334 334 # to use may be chosen automatically based on mpl configuration.
335 335 if self.pylab:
336 336 activate = self.shell.enable_pylab
337 337 if self.pylab == 'auto':
338 338 gui = None
339 339 else:
340 340 gui = self.pylab
341 341 else:
342 342 # Enable only GUI integration, no pylab
343 343 activate = inputhook.enable_gui
344 344
345 345 if gui or self.pylab:
346 346 try:
347 347 self.log.info("Enabling GUI event loop integration, "
348 348 "toolkit=%s, pylab=%s" % (gui, self.pylab) )
349 349 if self.pylab:
350 350 activate(gui, import_all=self.pylab_import_all)
351 351 else:
352 352 activate(gui)
353 353 except:
354 354 self.log.warn("Error in enabling GUI event loop integration:")
355 355 self.shell.showtraceback()
356 356
357 357 def start(self):
358 358 if self.subapp is not None:
359 359 return self.subapp.start()
360 360 # perform any prexec steps:
361 361 if self.interact:
362 362 self.log.debug("Starting IPython's mainloop...")
363 363 self.shell.mainloop()
364 364 else:
365 365 self.log.debug("IPython not interactive...")
366 366
367 367
368 368 def load_default_config(ipython_dir=None):
369 369 """Load the default config file from the default ipython_dir.
370 370
371 371 This is useful for embedded shells.
372 372 """
373 373 if ipython_dir is None:
374 374 ipython_dir = get_ipython_dir()
375 375 profile_dir = os.path.join(ipython_dir, 'profile_default')
376 376 cl = PyFileConfigLoader(default_config_file_name, profile_dir)
377 377 try:
378 378 config = cl.load_config()
379 379 except IOError:
380 380 # no config found
381 381 config = Config()
382 382 return config
383 383
384 384
385 385 def launch_new_instance():
386 386 """Create and run a full blown IPython instance"""
387 387 app = TerminalIPythonApp.instance()
388 388 app.initialize()
389 389 app.start()
390 390
391 391
392 392 if __name__ == '__main__':
393 393 launch_new_instance()
@@ -1,29 +1,30 b''
1 1 # encoding: utf-8
2 2 """
3 3 Extra capabilities for IPython
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 from IPython.lib.inputhook import (
18 18 enable_wx, disable_wx,
19 19 enable_gtk, disable_gtk,
20 20 enable_qt4, disable_qt4,
21 21 enable_tk, disable_tk,
22 enable_glut, disable_glut,
22 23 enable_pyglet, disable_pyglet,
23 24 set_inputhook, clear_inputhook,
24 25 current_gui
25 26 )
26 27
27 28 #-----------------------------------------------------------------------------
28 29 # Code
29 30 #-----------------------------------------------------------------------------
@@ -1,382 +1,585 b''
1 1 # coding: utf-8
2 2 """
3 3 Inputhook management for GUI event loop integration.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2008-2009 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 import ctypes
18 18 import sys
19 19 import warnings
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Constants
23 23 #-----------------------------------------------------------------------------
24 24
25 25 # Constants for identifying the GUI toolkits.
26 26 GUI_WX = 'wx'
27 27 GUI_QT = 'qt'
28 28 GUI_QT4 = 'qt4'
29 29 GUI_GTK = 'gtk'
30 30 GUI_TK = 'tk'
31 31 GUI_OSX = 'osx'
32 GUI_GLUT = 'glut'
32 33 GUI_PYGLET = 'pyglet'
33 34
34 35 #-----------------------------------------------------------------------------
35 36 # Utility classes
36 37 #-----------------------------------------------------------------------------
37 38
38 39
39 40 #-----------------------------------------------------------------------------
40 41 # Main InputHookManager class
41 42 #-----------------------------------------------------------------------------
42 43
43 44
44 45 class InputHookManager(object):
45 46 """Manage PyOS_InputHook for different GUI toolkits.
46 47
47 48 This class installs various hooks under ``PyOSInputHook`` to handle
48 49 GUI event loop integration.
49 50 """
50 51
51 52 def __init__(self):
52 53 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
53 54 self._apps = {}
54 55 self._reset()
55 56
56 57 def _reset(self):
57 58 self._callback_pyfunctype = None
58 59 self._callback = None
59 60 self._installed = False
60 61 self._current_gui = None
61 62
62 63 def get_pyos_inputhook(self):
63 64 """Return the current PyOS_InputHook as a ctypes.c_void_p."""
64 65 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
65 66
66 67 def get_pyos_inputhook_as_func(self):
67 68 """Return the current PyOS_InputHook as a ctypes.PYFUNCYPE."""
68 69 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
69 70
70 71 def set_inputhook(self, callback):
71 72 """Set PyOS_InputHook to callback and return the previous one."""
72 73 self._callback = callback
73 74 self._callback_pyfunctype = self.PYFUNC(callback)
74 75 pyos_inputhook_ptr = self.get_pyos_inputhook()
75 76 original = self.get_pyos_inputhook_as_func()
76 77 pyos_inputhook_ptr.value = \
77 78 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
78 79 self._installed = True
79 80 return original
80 81
81 82 def clear_inputhook(self, app=None):
82 83 """Set PyOS_InputHook to NULL and return the previous one.
83 84
84 85 Parameters
85 86 ----------
86 87 app : optional, ignored
87 88 This parameter is allowed only so that clear_inputhook() can be
88 89 called with a similar interface as all the ``enable_*`` methods. But
89 90 the actual value of the parameter is ignored. This uniform interface
90 91 makes it easier to have user-level entry points in the main IPython
91 92 app like :meth:`enable_gui`."""
92 93 pyos_inputhook_ptr = self.get_pyos_inputhook()
93 94 original = self.get_pyos_inputhook_as_func()
94 95 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
95 96 self._reset()
96 97 return original
97 98
98 99 def clear_app_refs(self, gui=None):
99 100 """Clear IPython's internal reference to an application instance.
100 101
101 102 Whenever we create an app for a user on qt4 or wx, we hold a
102 103 reference to the app. This is needed because in some cases bad things
103 104 can happen if a user doesn't hold a reference themselves. This
104 105 method is provided to clear the references we are holding.
105 106
106 107 Parameters
107 108 ----------
108 109 gui : None or str
109 110 If None, clear all app references. If ('wx', 'qt4') clear
110 111 the app for that toolkit. References are not held for gtk or tk
111 112 as those toolkits don't have the notion of an app.
112 113 """
113 114 if gui is None:
114 115 self._apps = {}
115 116 elif self._apps.has_key(gui):
116 117 del self._apps[gui]
117 118
118 119 def enable_wx(self, app=None):
119 120 """Enable event loop integration with wxPython.
120 121
121 122 Parameters
122 123 ----------
123 124 app : WX Application, optional.
124 125 Running application to use. If not given, we probe WX for an
125 126 existing application object, and create a new one if none is found.
126 127
127 128 Notes
128 129 -----
129 130 This methods sets the ``PyOS_InputHook`` for wxPython, which allows
130 131 the wxPython to integrate with terminal based applications like
131 132 IPython.
132 133
133 134 If ``app`` is not given we probe for an existing one, and return it if
134 135 found. If no existing app is found, we create an :class:`wx.App` as
135 136 follows::
136 137
137 138 import wx
138 139 app = wx.App(redirect=False, clearSigInt=False)
139 140 """
140 141 from IPython.lib.inputhookwx import inputhook_wx
141 142 self.set_inputhook(inputhook_wx)
142 143 self._current_gui = GUI_WX
143 144 import wx
144 145 if app is None:
145 146 app = wx.GetApp()
146 147 if app is None:
147 148 app = wx.App(redirect=False, clearSigInt=False)
148 149 app._in_event_loop = True
149 150 self._apps[GUI_WX] = app
150 151 return app
151 152
152 153 def disable_wx(self):
153 154 """Disable event loop integration with wxPython.
154 155
155 156 This merely sets PyOS_InputHook to NULL.
156 157 """
157 158 if self._apps.has_key(GUI_WX):
158 159 self._apps[GUI_WX]._in_event_loop = False
159 160 self.clear_inputhook()
160 161
161 162 def enable_qt4(self, app=None):
162 163 """Enable event loop integration with PyQt4.
163 164
164 165 Parameters
165 166 ----------
166 167 app : Qt Application, optional.
167 168 Running application to use. If not given, we probe Qt for an
168 169 existing application object, and create a new one if none is found.
169 170
170 171 Notes
171 172 -----
172 173 This methods sets the PyOS_InputHook for PyQt4, which allows
173 174 the PyQt4 to integrate with terminal based applications like
174 175 IPython.
175 176
176 177 If ``app`` is not given we probe for an existing one, and return it if
177 178 found. If no existing app is found, we create an :class:`QApplication`
178 179 as follows::
179 180
180 181 from PyQt4 import QtCore
181 182 app = QtGui.QApplication(sys.argv)
182 183 """
183 184 from IPython.external.qt_for_kernel import QtCore, QtGui
184 185
185 186 if 'pyreadline' in sys.modules:
186 187 # see IPython GitHub Issue #281 for more info on this issue
187 188 # Similar intermittent behavior has been reported on OSX,
188 189 # but not consistently reproducible
189 190 warnings.warn("""PyReadline's inputhook can conflict with Qt, causing delays
190 191 in interactive input. If you do see this issue, we recommend using another GUI
191 192 toolkit if you can, or disable readline with the configuration option
192 193 'TerminalInteractiveShell.readline_use=False', specified in a config file or
193 194 at the command-line""",
194 195 RuntimeWarning)
195 196
196 197 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
197 198 # was set when QtCore was imported, but if it ever got removed,
198 199 # you couldn't reset it. For earlier versions we can
199 200 # probably implement a ctypes version.
200 201 try:
201 202 QtCore.pyqtRestoreInputHook()
202 203 except AttributeError:
203 204 pass
204 205
205 206 self._current_gui = GUI_QT4
206 207 if app is None:
207 208 app = QtCore.QCoreApplication.instance()
208 209 if app is None:
209 210 app = QtGui.QApplication([" "])
210 211 app._in_event_loop = True
211 212 self._apps[GUI_QT4] = app
212 213 return app
213 214
214 215 def disable_qt4(self):
215 216 """Disable event loop integration with PyQt4.
216 217
217 218 This merely sets PyOS_InputHook to NULL.
218 219 """
219 220 if self._apps.has_key(GUI_QT4):
220 221 self._apps[GUI_QT4]._in_event_loop = False
221 222 self.clear_inputhook()
222 223
223 224 def enable_gtk(self, app=None):
224 225 """Enable event loop integration with PyGTK.
225 226
226 227 Parameters
227 228 ----------
228 229 app : ignored
229 230 Ignored, it's only a placeholder to keep the call signature of all
230 231 gui activation methods consistent, which simplifies the logic of
231 232 supporting magics.
232 233
233 234 Notes
234 235 -----
235 236 This methods sets the PyOS_InputHook for PyGTK, which allows
236 237 the PyGTK to integrate with terminal based applications like
237 238 IPython.
238 239 """
239 240 import gtk
240 241 try:
241 242 gtk.set_interactive(True)
242 243 self._current_gui = GUI_GTK
243 244 except AttributeError:
244 245 # For older versions of gtk, use our own ctypes version
245 246 from IPython.lib.inputhookgtk import inputhook_gtk
246 247 self.set_inputhook(inputhook_gtk)
247 248 self._current_gui = GUI_GTK
248 249
249 250 def disable_gtk(self):
250 251 """Disable event loop integration with PyGTK.
251 252
252 253 This merely sets PyOS_InputHook to NULL.
253 254 """
254 255 self.clear_inputhook()
255 256
256 257 def enable_tk(self, app=None):
257 258 """Enable event loop integration with Tk.
258 259
259 260 Parameters
260 261 ----------
261 262 app : toplevel :class:`Tkinter.Tk` widget, optional.
262 263 Running toplevel widget to use. If not given, we probe Tk for an
263 264 existing one, and create a new one if none is found.
264 265
265 266 Notes
266 267 -----
267 268 If you have already created a :class:`Tkinter.Tk` object, the only
268 269 thing done by this method is to register with the
269 270 :class:`InputHookManager`, since creating that object automatically
270 271 sets ``PyOS_InputHook``.
271 272 """
272 273 self._current_gui = GUI_TK
273 274 if app is None:
274 275 import Tkinter
275 276 app = Tkinter.Tk()
276 277 app.withdraw()
277 278 self._apps[GUI_TK] = app
278 279 return app
279 280
280 281 def disable_tk(self):
281 282 """Disable event loop integration with Tkinter.
282 283
283 284 This merely sets PyOS_InputHook to NULL.
284 285 """
285 286 self.clear_inputhook()
286 287
288 def enable_glut(self, app=None):
289 """Enable event loop integration with GLUT.
287 290
291 Parameters
292 ----------
293 app : ignored
294 Ignored, it's only a placeholder to keep the call signature of all
295 gui activation methods consistent, which simplifies the logic of
296 supporting magics.
297
298 Notes
299 -----
300
301 This methods sets the PyOS_InputHook for GLUT, which allows the GLUT to
302 integrate with terminal based applications like IPython. Due to GLUT
303 limitations, it is currently not possible to start the event loop
304 without first creating a window. You should thus not create another
305 window but use instead the created one. See 'gui-glut.py' in the
306 docs/examples/lib directory.
307
308 The default screen mode is set to:
309
310 glut.GLUT_DOUBLE | glut.GLUT_RGBA | glut.GLUT_DEPTH
311
312 Script integration
313 ------------------
314
315 if glut.glutGetWindow() > 0:
316 interactive = True
317 glut.glutShowWindow()
318 else:
319 interactive = False
320 glut.glutInit(sys.argv)
321 glut.glutInitDisplayMode( glut.GLUT_DOUBLE |
322 glut.GLUT_RGBA |
323 glut.GLUT_DEPTH )
324 ...
325 if not interactive:
326 glut.glutMainLoop()
327 """
328 # GLUT is quite an old library and it is difficult to ensure proper
329 # integration within IPython since original GLUT does not allow to handle
330 # events one by one. Instead, it requires for the mainloop to be entered
331 # and never returned (there is not even a function to exit he
332 # mainloop). Fortunately, there are alternatives such as freeglut
333 # (available for linux and windows) and the OSX implementation gives
334 # access to a glutCheckLoop() function that blocks itself until a new
335 # event is received. This means we have to setup a default timer to
336 # ensure we got at least one event that will unblock the function. We set
337 # a default timer of 60fps.
338 #
339 # Furthermore, it is not possible to install these handlers without a
340 # window being first created. We choose to make this window invisible and
341 # the user is supposed to make it visible when needed (see gui-glut.py in
342 # the docs/examples/lib directory). This means that display mode options
343 # are set at this level and user won't be able to change them later
344 # without modifying the code. This should probably be made available via
345 # IPython options system.
346
347 import OpenGL
348 OpenGL.ERROR_CHECKING = False
349 import OpenGL.GLUT as glut
350 import OpenGL.platform as platform
351 import time
352
353
354 # Frame per second : 60
355 # Should probably be an IPython option
356 glut_fps = 60
357
358
359 # Display mode : double buffeed + rgba + depth
360 # Should probably be an IPython option
361 glut_display_mode = (glut.GLUT_DOUBLE |
362 glut.GLUT_RGBA |
363 glut.GLUT_DEPTH)
364
365 glut_interrupted = False
366
367 def display():
368 ''' Dummy display function '''
369 pass
370
371 def timer(fps):
372 # We should normally set the active window to 1 and post a
373 # redisplay for each window. The problem is that we do not know
374 # how much active windows we have and there is no function in glut
375 # to get that number.
376 # glut.glutSetWindow(1)
377 glut.glutTimerFunc( int(1000.0/fps), timer, fps)
378 glut.glutPostRedisplay()
379
380 def close():
381 glut.glutHideWindow()
382
383 glutMainLoopEvent = None
384 if sys.platform == 'darwin':
385 try:
386 glutCheckLoop = platform.createBaseFunction(
387 'glutCheckLoop', dll=platform.GLUT, resultType=None,
388 argTypes=[],
389 doc='glutCheckLoop( ) -> None',
390 argNames=(),
391 )
392 except AttributeError:
393 raise RuntimeError(
394 '''Your glut implementation does not allow interactive sessions'''
395 '''Consider installing freeglut.''')
396 glutMainLoopEvent = glutCheckLoop
397 elif glut.HAVE_FREEGLUT:
398 glutMainLoopEvent = glut.glutMainLoopEvent
399 else:
400 raise RuntimeError(
401 '''Your glut implementation does not allow interactive sessions. '''
402 '''Consider installing freeglut.''')
403
404 def inputhook_glut():
405 """ Process pending GLUT events only. """
406
407 # We need to protect against a user pressing Control-C when IPython
408 # is idle and this is running. We should trap KeyboardInterrupt and
409 # pass but it does not seem to work with glutMainLoopEvent.
410 # Instead, we setup a signal handler on SIGINT and returns after
411 # having restored the default python SIGINT handler.
412 import signal
413 def handler(signum, frame):
414 signal.signal(signal.SIGINT, signal.default_int_handler)
415 print '\nKeyboardInterrupt'
416 # Need to reprint the prompt at this stage
417
418 signal.signal(signal.SIGINT, handler)
419
420 try:
421 glutMainLoopEvent()
422 except KeyboardInterrupt: # this catch doesn't work for some reasons...
423 pass
424
425 return 0
426
427 if not self._apps.has_key(GUI_GLUT):
428 glut.glutInit(sys.argv)
429 # Display mode should be also an Ipython option since user won't be able
430 # to change it later
431 glut.glutInitDisplayMode(glut_display_mode)
432 glut.glutCreateWindow(sys.argv[0])
433 glut.glutHideWindow()
434 glut.glutWMCloseFunc(close)
435 glut.glutDisplayFunc(display)
436 glut.glutTimerFunc( int(1000.0/glut_fps), timer, glut_fps)
437 else:
438 glut.glutWMCloseFunc(close)
439 glut.glutDisplayFunc(display)
440 glut.glutTimerFunc( int(1000.0/glut_fps), timer, glut_fps)
441
442 self.set_inputhook(inputhook_glut)
443 self._current_gui = GUI_GLUT
444 self._apps[GUI_GLUT] = True
445
446 def disable_glut(self):
447 """Disable event loop integration with glut.
448
449 This sets PyOS_InputHook to NULL and set the display function to a
450 dummy one and set the timer to a dummy timer that will be triggered
451 very far in the future.
452 """
453 import signal
454 import OpenGL
455 OpenGL.ERROR_CHECKING = False
456 import OpenGL.GLUT as glut
457 import OpenGL.platform as platform
458
459 def timer_none(fps):
460 ''' Dummy timer function '''
461 pass
462
463 glutMainLoopEvent = None
464 if sys.platform == 'darwin':
465 try:
466 glutCheckLoop = platform.createBaseFunction(
467 'glutCheckLoop', dll=platform.GLUT, resultType=None,
468 argTypes=[],
469 doc='glutCheckLoop( ) -> None',
470 argNames=(),
471 )
472 except AttributeError:
473 raise RuntimeError(
474 '''Your glut implementation does not allow interactive sessions'''
475 '''Consider installing freeglut.''')
476 glutMainLoopEvent = glutCheckLoop
477 elif glut.HAVE_FREEGLUT:
478 glutMainLoopEvent = glut.glutMainLoopEvent
479 else:
480 raise RuntimeError(
481 '''Your glut implementation does not allow interactive sessions. '''
482 '''Consider installing freeglut.''')
483
484 glut.glutHideWindow() # This is an event to be processed below
485 glutMainLoopEvent()
486 #glut.glutTimerFunc( sys.maxint-1, timer_none, 0)
487 self.clear_inputhook()
488 #signal.signal(signal.SIGINT, signal.default_int_handler)
288 489
289 490 def enable_pyglet(self, app=None):
290 491 """Enable event loop integration with pyglet.
291 492
292 493 Parameters
293 494 ----------
294 495 app : ignored
295 496 Ignored, it's only a placeholder to keep the call signature of all
296 497 gui activation methods consistent, which simplifies the logic of
297 498 supporting magics.
298 499
299 500 Notes
300 501 -----
301 502 This methods sets the ``PyOS_InputHook`` for pyglet, which allows
302 503 pyglet to integrate with terminal based applications like
303 504 IPython.
304 505
305 506 """
306 507 import pyglet
307 508 from IPython.lib.inputhookpyglet import inputhook_pyglet
308 509 self.set_inputhook(inputhook_pyglet)
309 510 self._current_gui = GUI_PYGLET
310 511 return app
311 512
312 513 def disable_pyglet(self):
313 514 """Disable event loop integration with pyglet.
314 515
315 516 This merely sets PyOS_InputHook to NULL.
316 517 """
317 518 self.clear_inputhook()
318 519
319
320 520 def current_gui(self):
321 521 """Return a string indicating the currently active GUI or None."""
322 522 return self._current_gui
323 523
324 524 inputhook_manager = InputHookManager()
325 525
326 526 enable_wx = inputhook_manager.enable_wx
327 527 disable_wx = inputhook_manager.disable_wx
328 528 enable_qt4 = inputhook_manager.enable_qt4
329 529 disable_qt4 = inputhook_manager.disable_qt4
330 530 enable_gtk = inputhook_manager.enable_gtk
331 531 disable_gtk = inputhook_manager.disable_gtk
332 532 enable_tk = inputhook_manager.enable_tk
333 533 disable_tk = inputhook_manager.disable_tk
534 enable_glut = inputhook_manager.enable_glut
535 disable_glut = inputhook_manager.disable_glut
334 536 enable_pyglet = inputhook_manager.enable_pyglet
335 537 disable_pyglet = inputhook_manager.disable_pyglet
336 538 clear_inputhook = inputhook_manager.clear_inputhook
337 539 set_inputhook = inputhook_manager.set_inputhook
338 540 current_gui = inputhook_manager.current_gui
339 541 clear_app_refs = inputhook_manager.clear_app_refs
340 542
341 543
342 544 # Convenience function to switch amongst them
343 545 def enable_gui(gui=None, app=None):
344 546 """Switch amongst GUI input hooks by name.
345 547
346 548 This is just a utility wrapper around the methods of the InputHookManager
347 549 object.
348 550
349 551 Parameters
350 552 ----------
351 553 gui : optional, string or None
352 554 If None, clears input hook, otherwise it must be one of the recognized
353 555 GUI names (see ``GUI_*`` constants in module).
354 556
355 557 app : optional, existing application object.
356 558 For toolkits that have the concept of a global app, you can supply an
357 559 existing one. If not given, the toolkit will be probed for one, and if
358 560 none is found, a new one will be created. Note that GTK does not have
359 561 this concept, and passing an app if `gui`=="GTK" will raise an error.
360 562
361 563 Returns
362 564 -------
363 565 The output of the underlying gui switch routine, typically the actual
364 566 PyOS_InputHook wrapper object or the GUI toolkit app created, if there was
365 567 one.
366 568 """
367 569 guis = {None: clear_inputhook,
368 570 GUI_OSX: lambda app=False: None,
369 571 GUI_TK: enable_tk,
370 572 GUI_GTK: enable_gtk,
371 573 GUI_WX: enable_wx,
372 574 GUI_QT: enable_qt4, # qt3 not supported
373 575 GUI_QT4: enable_qt4,
576 GUI_GLUT: enable_glut,
374 577 GUI_PYGLET: enable_pyglet,
375 578 }
376 579 try:
377 580 gui_hook = guis[gui]
378 581 except KeyError:
379 582 e = "Invalid GUI request %r, valid ones are:%s" % (gui, guis.keys())
380 583 raise ValueError(e)
381 584 return gui_hook(app)
382 585
General Comments 0
You need to be logged in to leave comments. Login now