Skip to content
This repository

Pyglet #743

Merged
merged 6 commits into from over 2 years ago

4 participants

Nicolas P. Rougier Min RK Fernando Perez jalalhugo
Nicolas P. Rougier

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

Min RK
Owner

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.

Fernando Perez
Owner

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:
https://github.com/ipython/ipython/blob/master/IPython/frontend/terminal/ipapp.py#L232

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.

jalalhugo

Adding the following lines to docs/examples/lib/gui-pyglet.py:

@window.event
def on_close():
window.close()

should fix the window closing issue.

Fernando Perez
Owner

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/inputhookpyglet.py", line 65, in inputhook_pyglet
    window.flip()
  File "/usr/lib/pymodules/python2.6/pyglet/window/xlib/__init__.py", line 797, in flip
    self._config._fbconfig, self._window, None)
AttributeError: 'NoneType' object has no attribute '_fbconfig'

Any idea what to do about that?

jalalhugo

A similar traceback has been reported here:

http://groups.google.com/group/pyglet-users/browse_thread/thread/47c1aab9aa4a3d23/c22f9e819826799e?#c22f9e819826799e

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

Fernando Perez
Owner

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 :)

Nicolas P. Rougier
Fernando Perez fperez merged commit db9640d into from September 13, 2011
Fernando Perez fperez closed this September 13, 2011
Fernando Perez
Owner

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
This page is out of date. Refresh to see the latest.
4  IPython/frontend/terminal/ipapp.py
@@ -229,8 +229,8 @@ def _quick_changed(self, name, old, 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'), config=True,
233  
-        help="Enable GUI event loop integration ('qt', 'wx', 'gtk')."
  232
+    gui = CaselessStrEnum(('qt','wx','gtk', 'pyglet'), config=True,
  233
+        help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'pyglet')."
234 234
     )
235 235
     pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'],
236 236
         config=True,
1  IPython/lib/__init__.py
@@ -19,6 +19,7 @@
19 19
     enable_gtk, disable_gtk,
20 20
     enable_qt4, disable_qt4,
21 21
     enable_tk, disable_tk,
  22
+    enable_pyglet, disable_pyglet,
22 23
     set_inputhook, clear_inputhook,
23 24
     current_gui
24 25
 )
40  IPython/lib/inputhook.py
@@ -29,6 +29,7 @@
29 29
 GUI_GTK = 'gtk'
30 30
 GUI_TK = 'tk'
31 31
 GUI_OSX = 'osx'
  32
+GUI_PYGLET = 'pyglet'
32 33
 
33 34
 #-----------------------------------------------------------------------------
34 35
 # Utility classes
@@ -283,6 +284,39 @@ def disable_tk(self):
283 284
         """
284 285
         self.clear_inputhook()
285 286
 
  287
+
  288
+
  289
+    def enable_pyglet(self, app=None):
  290
+        """Enable event loop integration with pyglet.
  291
+
  292
+        Parameters
  293
+        ----------
  294
+        app : ignored
  295
+           Ignored, it's only a placeholder to keep the call signature of all
  296
+           gui activation methods consistent, which simplifies the logic of
  297
+           supporting magics.
  298
+
  299
+        Notes
  300
+        -----
  301
+        This methods sets the ``PyOS_InputHook`` for pyglet, which allows
  302
+        pyglet to integrate with terminal based applications like
  303
+        IPython.
  304
+
  305
+        """
  306
+        import pyglet
  307
+        from IPython.lib.inputhookpyglet import inputhook_pyglet
  308
+        self.set_inputhook(inputhook_pyglet)
  309
+        self._current_gui = GUI_PYGLET
  310
+        return app
  311
+
  312
+    def disable_pyglet(self):
  313
+        """Disable event loop integration with pyglet.
  314
+
  315
+        This merely sets PyOS_InputHook to NULL.
  316
+        """
  317
+        self.clear_inputhook()
  318
+
  319
+
286 320
     def current_gui(self):
287 321
         """Return a string indicating the currently active GUI or None."""
288 322
         return self._current_gui
@@ -297,6 +331,8 @@ def current_gui(self):
297 331
 disable_gtk = inputhook_manager.disable_gtk
298 332
 enable_tk = inputhook_manager.enable_tk
299 333
 disable_tk = inputhook_manager.disable_tk
  334
+enable_pyglet = inputhook_manager.enable_pyglet
  335
+disable_pyglet = inputhook_manager.disable_pyglet
300 336
 clear_inputhook = inputhook_manager.clear_inputhook
301 337
 set_inputhook = inputhook_manager.set_inputhook
302 338
 current_gui = inputhook_manager.current_gui
@@ -334,7 +370,9 @@ def enable_gui(gui=None, app=None):
334 370
             GUI_GTK: enable_gtk,
335 371
             GUI_WX: enable_wx,
336 372
             GUI_QT: enable_qt4, # qt3 not supported
337  
-            GUI_QT4: enable_qt4 }
  373
+            GUI_QT4: enable_qt4,
  374
+            GUI_PYGLET: enable_pyglet,
  375
+            }
338 376
     try:
339 377
         gui_hook = guis[gui]
340 378
     except KeyError:
93  IPython/lib/inputhookpyglet.py
... ...
@@ -0,0 +1,93 @@
  1
+# encoding: utf-8
  2
+
  3
+"""
  4
+Enable pyglet to be used interacive by setting PyOS_InputHook.
  5
+
  6
+Authors:  Nicolas P. Rougier
  7
+"""
  8
+
  9
+#-----------------------------------------------------------------------------
  10
+#  Copyright (C) 2008-2009  The IPython Development Team
  11
+#
  12
+#  Distributed under the terms of the BSD License.  The full license is in
  13
+#  the file COPYING, distributed as part of this software.
  14
+#-----------------------------------------------------------------------------
  15
+
  16
+#-----------------------------------------------------------------------------
  17
+# Imports
  18
+#-----------------------------------------------------------------------------
  19
+
  20
+import os
  21
+import signal
  22
+import sys
  23
+import time
  24
+from timeit import default_timer as clock
  25
+import pyglet
  26
+
  27
+if os.name == 'posix':
  28
+    import select
  29
+elif sys.platform == 'win32':
  30
+    import msvcrt
  31
+
  32
+#-----------------------------------------------------------------------------
  33
+# Code
  34
+#-----------------------------------------------------------------------------
  35
+
  36
+def stdin_ready():
  37
+    if os.name == 'posix':
  38
+        infds, outfds, erfds = select.select([sys.stdin],[],[],0)
  39
+        if infds:
  40
+            return True
  41
+        else:
  42
+            return False
  43
+    elif sys.platform == 'win32':
  44
+        return msvcrt.kbhit()
  45
+
  46
+
  47
+def inputhook_pyglet():
  48
+    """Run the pyglet event loop by processing pending events only.
  49
+    
  50
+    This keeps processing pending events until stdin is ready.  After
  51
+    processing all pending events, a call to time.sleep is inserted.  This is
  52
+    needed, otherwise, CPU usage is at 100%.  This sleep time should be tuned
  53
+    though for best performance.
  54
+    """
  55
+    # We need to protect against a user pressing Control-C when IPython is
  56
+    # idle and this is running. We trap KeyboardInterrupt and pass.
  57
+    try:
  58
+        t = clock()
  59
+        while not stdin_ready():
  60
+            pyglet.clock.tick()
  61
+            for window in pyglet.app.windows:
  62
+                window.switch_to()
  63
+                window.dispatch_events()
  64
+                window.dispatch_event('on_draw')
  65
+                window.flip()
  66
+
  67
+            # We need to sleep at this point to keep the idle CPU load
  68
+            # low.  However, if sleep to long, GUI response is poor.  As 
  69
+            # a compromise, we watch how often GUI events are being processed
  70
+            # and switch between a short and long sleep time.  Here are some
  71
+            # stats useful in helping to tune this.
  72
+            # time    CPU load
  73
+            # 0.001   13%
  74
+            # 0.005   3%
  75
+            # 0.01    1.5%
  76
+            # 0.05    0.5%
  77
+            used_time = clock() - t
  78
+            if used_time > 5*60.0:
  79
+                # print 'Sleep for 5 s'  # dbg
  80
+                time.sleep(5.0)
  81
+            elif used_time > 10.0:
  82
+                # print 'Sleep for 1 s'  # dbg
  83
+                time.sleep(1.0)
  84
+            elif used_time > 0.1:
  85
+                # Few GUI events coming in, so we can sleep longer
  86
+                # print 'Sleep for 0.05 s'  # dbg
  87
+                time.sleep(0.05)
  88
+            else:
  89
+                # Many GUI events coming in, so sleep only very little
  90
+                time.sleep(0.001)
  91
+    except KeyboardInterrupt:
  92
+        pass
  93
+    return 0
33  docs/examples/lib/gui-pyglet.py
... ...
@@ -0,0 +1,33 @@
  1
+#!/usr/bin/env python
  2
+"""Simple pyglet example to manually test event loop integration.
  3
+
  4
+This is meant to run tests manually in ipython as:
  5
+
  6
+In [5]: %gui pyglet
  7
+
  8
+In [6]: %run gui-pyglet.py
  9
+"""
  10
+
  11
+import pyglet
  12
+
  13
+
  14
+window = pyglet.window.Window()
  15
+label = pyglet.text.Label('Hello, world',
  16
+                          font_name='Times New Roman',
  17
+                          font_size=36,
  18
+                          x=window.width//2, y=window.height//2,
  19
+                          anchor_x='center', anchor_y='center')
  20
+@window.event
  21
+def on_close():
  22
+    window.close()
  23
+
  24
+@window.event
  25
+def on_draw():
  26
+    window.clear()
  27
+    label.draw()
  28
+
  29
+try:
  30
+    from IPython.lib.inputhook import enable_pyglet
  31
+    enable_pyglet()
  32
+except ImportError:
  33
+    pyglet.app.run()
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.