Skip to content

Commit

Permalink
Merge branch 'pyglet' by Nicolas Rougier, with minor fixes.
Browse files Browse the repository at this point in the history
This adds pyglet event loop integration with a new 'pyglet' option for
the --gui flag and %gui magic.  The internal implementation is fairly
similar to the one for Wx integration.
  • Loading branch information
fperez committed Sep 13, 2011
2 parents d5d1644 + fe30ce1 commit 7511bc6
Show file tree
Hide file tree
Showing 5 changed files with 190 additions and 3 deletions.
4 changes: 2 additions & 2 deletions IPython/frontend/terminal/ipapp.py
Expand Up @@ -229,8 +229,8 @@ def _quick_changed(self, name, old, new):
self.load_config_file = lambda *a, **kw: None
self.ignore_old_config=True

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'],
config=True,
Expand Down
1 change: 1 addition & 0 deletions IPython/lib/__init__.py
Expand Up @@ -19,6 +19,7 @@
enable_gtk, disable_gtk,
enable_qt4, disable_qt4,
enable_tk, disable_tk,
enable_pyglet, disable_pyglet,
set_inputhook, clear_inputhook,
current_gui
)
Expand Down
40 changes: 39 additions & 1 deletion IPython/lib/inputhook.py
Expand Up @@ -29,6 +29,7 @@
GUI_GTK = 'gtk'
GUI_TK = 'tk'
GUI_OSX = 'osx'
GUI_PYGLET = 'pyglet'

#-----------------------------------------------------------------------------
# Utility classes
Expand Down Expand Up @@ -283,6 +284,39 @@ def disable_tk(self):
"""
self.clear_inputhook()



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
Expand All @@ -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
Expand Down Expand Up @@ -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,
}
try:
gui_hook = guis[gui]
except KeyError:
Expand Down
115 changes: 115 additions & 0 deletions IPython/lib/inputhookpyglet.py
@@ -0,0 +1,115 @@
# encoding: utf-8
"""
Enable pyglet to be used interacive by setting PyOS_InputHook.
Authors
-------
* Nicolas P. Rougier
* Fernando Perez
"""

#-----------------------------------------------------------------------------
# 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

#-----------------------------------------------------------------------------
# Platform-dependent imports and functions
#-----------------------------------------------------------------------------

if os.name == 'posix':
import select

def stdin_ready():
infds, outfds, erfds = select.select([sys.stdin],[],[],0)
if infds:
return True
else:
return False

elif sys.platform == 'win32':
import msvcrt

def stdin_ready():
return msvcrt.kbhit()


# On linux only, window.flip() has a bug that causes an AttributeError on
# window close. For details, see:
# http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e

if sys.platform.startswith('linux'):
def flip(window):
try:
window.flip()
except AttributeError:
pass
else:
def flip(window):
window.flip()

#-----------------------------------------------------------------------------
# Code
#-----------------------------------------------------------------------------

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 pyglet.app.windows:
window.switch_to()
window.dispatch_events()
window.dispatch_event('on_draw')
flip(window)

# 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 changes: 33 additions & 0 deletions docs/examples/lib/gui-pyglet.py
@@ -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 gui-pyglet.py
"""

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')
@window.event
def on_close():
window.close()

@window.event
def on_draw():
window.clear()
label.draw()

try:
from IPython.lib.inputhook import enable_pyglet
enable_pyglet()
except ImportError:
pyglet.app.run()

0 comments on commit 7511bc6

Please sign in to comment.