Skip to content


Pyglet #743

merged 6 commits into from

4 participants


This is the experimental pyglet event loop integration.
It requires pyglet.

IPython member

I'm not experienced with pyglet, but this looks fine to me. You may want to fix the one remaining 'wxPython' hanging around in inputhook, from the copy/paste from the wx section.

IPython member

This looks good overall, but I can't seem to close the window of the demo applet when I run it. Do you know why? Ideally, we'd offer users the ability to run a script, close all of its windows and then run it again as many times as they want, without ever losing the interactive shell. That works right now for the wx/gtk/qt event loops, so let's try to get the two GL ones in similar shape, if possible.

Also, fixing the wx references Min pointed out would be good.

Finally, if you can figure out what's going on with the window closing, we only have one more issue to sort out: any option supported by the gui magic should also be supported by the --gui command-line flag, so users can start ipython in gui mode if they want to from the beginning.

The code that defines these options is here:

so it should gain a 'pyglet' option. Once that is working, it should be 100% equivalent to type at the system shell:

ipython --gui pyglet

or at the IPython prompt:

%gui pyglet

Thanks a lot for the good work! I realize these GL event loops are proving a little hard to get right, but hopefully we can finish the process and merge both of your pull requests. We just want to make sure they work reliably and correctly in the intended manner. If ultimately some of it can't be made to work that's OK, but then we'd want to add code that informs the user in a visible manner what the limitations are of a given GL option.


Adding the following lines to docs/examples/lib/

def on_close():

should fix the window closing issue.

IPython member

Thanks for the tip! It almost works, as in that now I can close the window, but then a nasty traceback goes to the terminal with:

Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 295, in 'calling callback function'
  File "/home/fperez/usr/lib/python2.6/site-packages/IPython/lib/", line 65, in inputhook_pyglet
  File "/usr/lib/pymodules/python2.6/pyglet/window/xlib/", line 797, in flip
    self._config._fbconfig, self._window, None)
AttributeError: 'NoneType' object has no attribute '_fbconfig'

Any idea what to do about that?


A similar traceback has been reported here:

Apparently it does not occur on Windows, and I can't reproduce it either on my mac.

IPython member

Thanks for the info @jalalhugo, it looks to be a pyglet bug then. If anyone knows how to work around it, that would be great; otherwise we'll just have to live with it in linux (it doesn't actually crash ipython, it just dumps a bunch of info to the screen).

With that info, @rougier, I think we can proceed as follows:

  • go ahead and incorporate the suggestion above into the example
  • add support for --gui pyglet at the command line as indicated above.

Let me know if you need any help. It seems we'll be able to merge this one pretty soon then :)

@fperez fperez merged commit db9640d into ipython:master
IPython member

Great, thanks! If you look, I added one more commit with a few minor tweaks: fe30ce1. This way we silence that annoying traceback on linux, without slowing down other platforms with a try/except.

I also changed the definition of stdin_ready() to make it platform dependent, that way the platform check happens only once when it's loaded instead of on every call.

Thanks again for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
4 IPython/frontend/terminal/
@@ -229,8 +229,8 @@ def _quick_changed(self, name, old, new):
self.load_config_file = lambda *a, **kw: None
- gui = CaselessStrEnum(('qt','wx','gtk'), config=True,
- help="Enable GUI event loop integration ('qt', 'wx', 'gtk')."
+ gui = CaselessStrEnum(('qt','wx','gtk', 'pyglet'), config=True,
+ help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'pyglet')."
pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
1 IPython/lib/
@@ -19,6 +19,7 @@
enable_gtk, disable_gtk,
enable_qt4, disable_qt4,
enable_tk, disable_tk,
+ enable_pyglet, disable_pyglet,
set_inputhook, clear_inputhook,
40 IPython/lib/
@@ -29,6 +29,7 @@
GUI_GTK = 'gtk'
GUI_TK = 'tk'
GUI_OSX = 'osx'
+GUI_PYGLET = 'pyglet'
# Utility classes
@@ -283,6 +284,39 @@ def disable_tk(self):
+ def enable_pyglet(self, app=None):
+ """Enable event loop integration with pyglet.
+ Parameters
+ ----------
+ app : ignored
+ Ignored, it's only a placeholder to keep the call signature of all
+ gui activation methods consistent, which simplifies the logic of
+ supporting magics.
+ Notes
+ -----
+ This methods sets the ``PyOS_InputHook`` for pyglet, which allows
+ pyglet to integrate with terminal based applications like
+ IPython.
+ """
+ import pyglet
+ from IPython.lib.inputhookpyglet import inputhook_pyglet
+ self.set_inputhook(inputhook_pyglet)
+ self._current_gui = GUI_PYGLET
+ return app
+ def disable_pyglet(self):
+ """Disable event loop integration with pyglet.
+ This merely sets PyOS_InputHook to NULL.
+ """
+ self.clear_inputhook()
def current_gui(self):
"""Return a string indicating the currently active GUI or None."""
return self._current_gui
@@ -297,6 +331,8 @@ def current_gui(self):
disable_gtk = inputhook_manager.disable_gtk
enable_tk = inputhook_manager.enable_tk
disable_tk = inputhook_manager.disable_tk
+enable_pyglet = inputhook_manager.enable_pyglet
+disable_pyglet = inputhook_manager.disable_pyglet
clear_inputhook = inputhook_manager.clear_inputhook
set_inputhook = inputhook_manager.set_inputhook
current_gui = inputhook_manager.current_gui
@@ -334,7 +370,9 @@ def enable_gui(gui=None, app=None):
GUI_GTK: enable_gtk,
GUI_WX: enable_wx,
GUI_QT: enable_qt4, # qt3 not supported
- GUI_QT4: enable_qt4 }
+ GUI_QT4: enable_qt4,
+ GUI_PYGLET: enable_pyglet,
+ }
gui_hook = guis[gui]
except KeyError:
93 IPython/lib/
@@ -0,0 +1,93 @@
+# encoding: utf-8
+Enable pyglet to be used interacive by setting PyOS_InputHook.
+Authors: Nicolas P. Rougier
+# Copyright (C) 2008-2009 The IPython Development Team
+# Distributed under the terms of the BSD License. The full license is in
+# the file COPYING, distributed as part of this software.
+# Imports
+import os
+import signal
+import sys
+import time
+from timeit import default_timer as clock
+import pyglet
+if == 'posix':
+ import select
+elif sys.platform == 'win32':
+ import msvcrt
+# Code
+def stdin_ready():
+ if == 'posix':
+ infds, outfds, erfds =[sys.stdin],[],[],0)
+ if infds:
+ return True
+ else:
+ return False
+ elif sys.platform == 'win32':
+ return msvcrt.kbhit()
+def inputhook_pyglet():
+ """Run the pyglet event loop by processing pending events only.
+ This keeps processing pending events until stdin is ready. After
+ processing all pending events, a call to time.sleep is inserted. This is
+ needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
+ though for best performance.
+ """
+ # We need to protect against a user pressing Control-C when IPython is
+ # idle and this is running. We trap KeyboardInterrupt and pass.
+ try:
+ t = clock()
+ while not stdin_ready():
+ pyglet.clock.tick()
+ for window in
+ window.switch_to()
+ window.dispatch_events()
+ window.dispatch_event('on_draw')
+ window.flip()
+ # We need to sleep at this point to keep the idle CPU load
+ # low. However, if sleep to long, GUI response is poor. As
+ # a compromise, we watch how often GUI events are being processed
+ # and switch between a short and long sleep time. Here are some
+ # stats useful in helping to tune this.
+ # time CPU load
+ # 0.001 13%
+ # 0.005 3%
+ # 0.01 1.5%
+ # 0.05 0.5%
+ used_time = clock() - t
+ if used_time > 5*60.0:
+ # print 'Sleep for 5 s' # dbg
+ time.sleep(5.0)
+ elif used_time > 10.0:
+ # print 'Sleep for 1 s' # dbg
+ time.sleep(1.0)
+ elif used_time > 0.1:
+ # Few GUI events coming in, so we can sleep longer
+ # print 'Sleep for 0.05 s' # dbg
+ time.sleep(0.05)
+ else:
+ # Many GUI events coming in, so sleep only very little
+ time.sleep(0.001)
+ except KeyboardInterrupt:
+ pass
+ return 0
33 docs/examples/lib/
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+"""Simple pyglet example to manually test event loop integration.
+This is meant to run tests manually in ipython as:
+In [5]: %gui pyglet
+In [6]: %run
+import pyglet
+window = pyglet.window.Window()
+label = pyglet.text.Label('Hello, world',
+ font_name='Times New Roman',
+ font_size=36,
+ x=window.width//2, y=window.height//2,
+ anchor_x='center', anchor_y='center')
+def on_close():
+ window.close()
+def on_draw():
+ window.clear()
+ label.draw()
+ from IPython.lib.inputhook import enable_pyglet
+ enable_pyglet()
+except ImportError:
Something went wrong with that request. Please try again.