##// END OF EJS Templates
First draft of full inputhook management.
Brian Granger -
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,109 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 Inputhook management for GUI event loop integration.
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 import ctypes
19
20 #-----------------------------------------------------------------------------
21 # Code
22 #-----------------------------------------------------------------------------
23
24
25 class InputHookManager(object):
26
27 def __init__(self):
28 self.PYFUNC = ctypes.PYFUNCTYPE(ctypes.c_int)
29 self._reset()
30
31 def _reset(self):
32 self._callback_pyfunctype = None
33 self._callback = None
34 self._installed = False
35
36 def get_pyos_inputhook(self):
37 return ctypes.c_void_p.in_dll(ctypes.pythonapi,"PyOS_InputHook")
38
39 def get_pyos_inputhook_as_func(self):
40 return self.PYFUNC.in_dll(ctypes.pythonapi,"PyOS_InputHook")
41
42 def set_inputhook(callback):
43 """Set PyOS_InputHook to callback and return the previous one.
44 """
45 self._callback = callback
46 self._callback_pyfunctype = self.PYFUNC(callback)
47 pyos_inputhook_ptr = self.get_pyos_inputhook()
48 original = self.get_pyos_inputhook_as_func()
49 pyos_inputhook_ptr.value = \
50 ctypes.cast(self._callback_pyfunctype, ctypes.c_void_p).value
51 self._installed = True
52 return original
53
54 def clear_inputhook(self):
55 """Set PyOS_InputHook to NULL and return the previous one."""
56 pyos_inputhook_ptr = self.get_pyos_inputhook()
57 original = self.get_pyos_inputhook_as_func()
58 pyos_inputhook_ptr.value = ctypes.c_void_p(None).value
59 self._reset()
60 return original
61
62 def enable_wx(self):
63 from IPython.lib.guiloop.inputhookwx import inputhook_wx
64 self.set_inputhook(inputhook_wx)
65
66 def disable_wx(self):
67 self.clear_inputhook()
68
69 def enable_qt4(self):
70 from PyQt4 import QtCore
71 # PyQt4 has had this since 4.3.1. In version 4.2, PyOS_InputHook
72 # was set when QtCore was imported, but if it ever got removed,
73 # you couldn't reset it. For earlier versions we can
74 # probably implement a ctypes version.
75 try:
76 QtCore.pyqtRestoreInputHook()
77 except AttributeError:
78 pass
79
80 def disable_qt4(self):
81 self.clear_inputhook()
82
83 def enable_gtk(self):
84 import gtk
85 try:
86 gtk.set_interactive(True)
87 except AttributeError:
88 # For older versions of gtk, use our own ctypes version
89 from IPython.lib.guiloop.inputhookgtk import inputhook_gtk
90 add_inputhook(inputhook_gtk)
91
92 def disable_gtk(self):
93 self.clear_inputhook()
94
95 def enable_tk(self):
96 # Creating a Tkinter.Tk object sets PyOS_InputHook()
97 pass
98
99 def disable_tk(self):
100 self.clear_inputhook()
101
102 inputhook_manager = InputHookManager()
103
104 enable_wx = inputhook_manager.enable_wx
105 disable_wx = inputhook_manager.disable_wx
106 enable_qt4 = inputhook_manager.enable_qt4
107 disable_qt4 = inputhook_manager.disable_qt4
108 enable_gtk = inputhook_manager.enable_gtk
109 disable_gtk = inputhook_manager.disable_gtk
@@ -0,0 +1,28 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 Enable pygtk to be used interacive by setting PyOS_InputHook.
5
6 Authors: Brian Granger
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 import sys
17 import gtk, gobject
18
19
20 def _main_quit(*args, **kwargs):
21 gtk.main_quit()
22 return False
23
24 def inputhook_gtk():
25 gobject.io_add_watch(sys.stdin, gobject.IO_IN, _main_quit)
26 gtk.main()
27 return 0
28
@@ -0,0 +1,153 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3
4 """
5 Enable wxPython to be used interacive by setting PyOS_InputHook.
6
7 Authors: Robin Dunn, Brian Granger, Ondrej Certik
8 """
9
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2009 The IPython Development Team
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
16
17 #-----------------------------------------------------------------------------
18 # Imports
19 #-----------------------------------------------------------------------------
20
21 import os
22 import sys
23 import time
24 from timeit import default_timer as clock
25 import wx
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_wx1():
48 """Run the wx event loop by processing pending events only.
49
50 This approach seems to work, but its performance is not great as it
51 relies on having PyOS_InputHook called regularly.
52 """
53 app = wx.GetApp()
54 if app is not None:
55 assert wx.Thread_IsMain()
56
57 # Make a temporary event loop and process system events until
58 # there are no more waiting, then allow idle events (which
59 # will also deal with pending or posted wx events.)
60 evtloop = wx.EventLoop()
61 ea = wx.EventLoopActivator(evtloop)
62 while evtloop.Pending():
63 evtloop.Dispatch()
64 app.ProcessIdle()
65 del ea
66 return 0
67
68 class EventLoopTimer(wx.Timer):
69
70 def __init__(self, func):
71 self.func = func
72 wx.Timer.__init__(self)
73
74 def Notify(self):
75 self.func()
76
77 class EventLoopRunner(object):
78
79 def Run(self, time):
80 self.evtloop = wx.EventLoop()
81 self.timer = EventLoopTimer(self.check_stdin)
82 self.timer.Start(time)
83 self.evtloop.Run()
84
85 def check_stdin(self):
86 if stdin_ready():
87 self.timer.Stop()
88 self.evtloop.Exit()
89
90 def inputhook_wx2():
91 """Run the wx event loop, polling for stdin.
92
93 This version runs the wx eventloop for an undetermined amount of time,
94 during which it periodically checks to see if anything is ready on
95 stdin. If anything is ready on stdin, the event loop exits.
96
97 The argument to elr.Run controls how often the event loop looks at stdin.
98 This determines the responsiveness at the keyboard. A setting of 1000
99 enables a user to type at most 1 char per second. I have found that a
100 setting of 10 gives good keyboard response. We can shorten it further,
101 but eventually performance would suffer from calling select/kbhit too
102 often.
103 """
104 app = wx.GetApp()
105 if app is not None:
106 assert wx.Thread_IsMain()
107 elr = EventLoopRunner()
108 # As this time is made shorter, keyboard response improves, but idle
109 # CPU load goes up. 10 ms seems like a good compromise.
110 elr.Run(time=10) # CHANGE time here to control polling interval
111 return 0
112
113 def inputhook_wx3():
114 """Run the wx event loop by processing pending events only.
115
116 This is like inputhook_wx1, but it keeps processing pending events
117 until stdin is ready. After processing all pending events, a call to
118 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
119 This sleep time should be tuned though for best performance.
120 """
121 app = wx.GetApp()
122 if app is not None:
123 assert wx.Thread_IsMain()
124
125 evtloop = wx.EventLoop()
126 ea = wx.EventLoopActivator(evtloop)
127 t = clock()
128 while not stdin_ready():
129 while evtloop.Pending():
130 t = clock()
131 evtloop.Dispatch()
132 app.ProcessIdle()
133 # We need to sleep at this point to keep the idle CPU load
134 # low. However, if sleep to long, GUI response is poor. As
135 # a compromise, we watch how often GUI events are being processed
136 # and switch between a short and long sleep time. Here are some
137 # stats useful in helping to tune this.
138 # time CPU load
139 # 0.001 13%
140 # 0.005 3%
141 # 0.01 1.5%
142 # 0.05 0.5%
143 if clock()-t > 0.1:
144 # Few GUI events coming in, so we can sleep longer
145 time.sleep(0.05)
146 else:
147 # Many GUI events coming in, so sleep only very little
148 time.sleep(0.001)
149 del ea
150 return 0
151
152 # This is our default implementation
153 inputhook_wx = inputhook_wx3 No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now