##// END OF EJS Templates
Backport PR #2901: Fix inputhook_wx on osx...
MinRK -
Show More
@@ -1,165 +1,171 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """
3 """
4 Enable wxPython to be used interacive by setting PyOS_InputHook.
4 Enable wxPython to be used interacive by setting PyOS_InputHook.
5
5
6 Authors: Robin Dunn, Brian Granger, Ondrej Certik
6 Authors: Robin Dunn, Brian Granger, Ondrej Certik
7 """
7 """
8
8
9 #-----------------------------------------------------------------------------
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2011 The IPython Development Team
10 # Copyright (C) 2008-2011 The IPython Development Team
11 #
11 #
12 # Distributed under the terms of the BSD License. The full license is in
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Imports
17 # Imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19
19
20 import os
20 import os
21 import signal
21 import signal
22 import sys
22 import sys
23 import time
23 import time
24 from timeit import default_timer as clock
24 from timeit import default_timer as clock
25 import wx
25 import wx
26
26
27 from IPython.lib.inputhook import stdin_ready
27 from IPython.lib.inputhook import stdin_ready
28
28
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Code
31 # Code
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 def inputhook_wx1():
34 def inputhook_wx1():
35 """Run the wx event loop by processing pending events only.
35 """Run the wx event loop by processing pending events only.
36
36
37 This approach seems to work, but its performance is not great as it
37 This approach seems to work, but its performance is not great as it
38 relies on having PyOS_InputHook called regularly.
38 relies on having PyOS_InputHook called regularly.
39 """
39 """
40 try:
40 try:
41 app = wx.GetApp()
41 app = wx.GetApp()
42 if app is not None:
42 if app is not None:
43 assert wx.Thread_IsMain()
43 assert wx.Thread_IsMain()
44
44
45 # Make a temporary event loop and process system events until
45 # Make a temporary event loop and process system events until
46 # there are no more waiting, then allow idle events (which
46 # there are no more waiting, then allow idle events (which
47 # will also deal with pending or posted wx events.)
47 # will also deal with pending or posted wx events.)
48 evtloop = wx.EventLoop()
48 evtloop = wx.EventLoop()
49 ea = wx.EventLoopActivator(evtloop)
49 ea = wx.EventLoopActivator(evtloop)
50 while evtloop.Pending():
50 while evtloop.Pending():
51 evtloop.Dispatch()
51 evtloop.Dispatch()
52 app.ProcessIdle()
52 app.ProcessIdle()
53 del ea
53 del ea
54 except KeyboardInterrupt:
54 except KeyboardInterrupt:
55 pass
55 pass
56 return 0
56 return 0
57
57
58 class EventLoopTimer(wx.Timer):
58 class EventLoopTimer(wx.Timer):
59
59
60 def __init__(self, func):
60 def __init__(self, func):
61 self.func = func
61 self.func = func
62 wx.Timer.__init__(self)
62 wx.Timer.__init__(self)
63
63
64 def Notify(self):
64 def Notify(self):
65 self.func()
65 self.func()
66
66
67 class EventLoopRunner(object):
67 class EventLoopRunner(object):
68
68
69 def Run(self, time):
69 def Run(self, time):
70 self.evtloop = wx.EventLoop()
70 self.evtloop = wx.EventLoop()
71 self.timer = EventLoopTimer(self.check_stdin)
71 self.timer = EventLoopTimer(self.check_stdin)
72 self.timer.Start(time)
72 self.timer.Start(time)
73 self.evtloop.Run()
73 self.evtloop.Run()
74
74
75 def check_stdin(self):
75 def check_stdin(self):
76 if stdin_ready():
76 if stdin_ready():
77 self.timer.Stop()
77 self.timer.Stop()
78 self.evtloop.Exit()
78 self.evtloop.Exit()
79
79
80 def inputhook_wx2():
80 def inputhook_wx2():
81 """Run the wx event loop, polling for stdin.
81 """Run the wx event loop, polling for stdin.
82
82
83 This version runs the wx eventloop for an undetermined amount of time,
83 This version runs the wx eventloop for an undetermined amount of time,
84 during which it periodically checks to see if anything is ready on
84 during which it periodically checks to see if anything is ready on
85 stdin. If anything is ready on stdin, the event loop exits.
85 stdin. If anything is ready on stdin, the event loop exits.
86
86
87 The argument to elr.Run controls how often the event loop looks at stdin.
87 The argument to elr.Run controls how often the event loop looks at stdin.
88 This determines the responsiveness at the keyboard. A setting of 1000
88 This determines the responsiveness at the keyboard. A setting of 1000
89 enables a user to type at most 1 char per second. I have found that a
89 enables a user to type at most 1 char per second. I have found that a
90 setting of 10 gives good keyboard response. We can shorten it further,
90 setting of 10 gives good keyboard response. We can shorten it further,
91 but eventually performance would suffer from calling select/kbhit too
91 but eventually performance would suffer from calling select/kbhit too
92 often.
92 often.
93 """
93 """
94 try:
94 try:
95 app = wx.GetApp()
95 app = wx.GetApp()
96 if app is not None:
96 if app is not None:
97 assert wx.Thread_IsMain()
97 assert wx.Thread_IsMain()
98 elr = EventLoopRunner()
98 elr = EventLoopRunner()
99 # As this time is made shorter, keyboard response improves, but idle
99 # As this time is made shorter, keyboard response improves, but idle
100 # CPU load goes up. 10 ms seems like a good compromise.
100 # CPU load goes up. 10 ms seems like a good compromise.
101 elr.Run(time=10) # CHANGE time here to control polling interval
101 elr.Run(time=10) # CHANGE time here to control polling interval
102 except KeyboardInterrupt:
102 except KeyboardInterrupt:
103 pass
103 pass
104 return 0
104 return 0
105
105
106 def inputhook_wx3():
106 def inputhook_wx3():
107 """Run the wx event loop by processing pending events only.
107 """Run the wx event loop by processing pending events only.
108
108
109 This is like inputhook_wx1, but it keeps processing pending events
109 This is like inputhook_wx1, but it keeps processing pending events
110 until stdin is ready. After processing all pending events, a call to
110 until stdin is ready. After processing all pending events, a call to
111 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
111 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
112 This sleep time should be tuned though for best performance.
112 This sleep time should be tuned though for best performance.
113 """
113 """
114 # We need to protect against a user pressing Control-C when IPython is
114 # We need to protect against a user pressing Control-C when IPython is
115 # idle and this is running. We trap KeyboardInterrupt and pass.
115 # idle and this is running. We trap KeyboardInterrupt and pass.
116 try:
116 try:
117 app = wx.GetApp()
117 app = wx.GetApp()
118 if app is not None:
118 if app is not None:
119 assert wx.Thread_IsMain()
119 assert wx.Thread_IsMain()
120
120
121 # The import of wx on Linux sets the handler for signal.SIGINT
121 # The import of wx on Linux sets the handler for signal.SIGINT
122 # to 0. This is a bug in wx or gtk. We fix by just setting it
122 # to 0. This is a bug in wx or gtk. We fix by just setting it
123 # back to the Python default.
123 # back to the Python default.
124 if not callable(signal.getsignal(signal.SIGINT)):
124 if not callable(signal.getsignal(signal.SIGINT)):
125 signal.signal(signal.SIGINT, signal.default_int_handler)
125 signal.signal(signal.SIGINT, signal.default_int_handler)
126
126
127 evtloop = wx.EventLoop()
127 evtloop = wx.EventLoop()
128 ea = wx.EventLoopActivator(evtloop)
128 ea = wx.EventLoopActivator(evtloop)
129 t = clock()
129 t = clock()
130 while not stdin_ready():
130 while not stdin_ready():
131 while evtloop.Pending():
131 while evtloop.Pending():
132 t = clock()
132 t = clock()
133 evtloop.Dispatch()
133 evtloop.Dispatch()
134 app.ProcessIdle()
134 app.ProcessIdle()
135 # We need to sleep at this point to keep the idle CPU load
135 # We need to sleep at this point to keep the idle CPU load
136 # low. However, if sleep to long, GUI response is poor. As
136 # low. However, if sleep to long, GUI response is poor. As
137 # a compromise, we watch how often GUI events are being processed
137 # a compromise, we watch how often GUI events are being processed
138 # and switch between a short and long sleep time. Here are some
138 # and switch between a short and long sleep time. Here are some
139 # stats useful in helping to tune this.
139 # stats useful in helping to tune this.
140 # time CPU load
140 # time CPU load
141 # 0.001 13%
141 # 0.001 13%
142 # 0.005 3%
142 # 0.005 3%
143 # 0.01 1.5%
143 # 0.01 1.5%
144 # 0.05 0.5%
144 # 0.05 0.5%
145 used_time = clock() - t
145 used_time = clock() - t
146 if used_time > 5*60.0:
146 if used_time > 5*60.0:
147 # print 'Sleep for 5 s' # dbg
147 # print 'Sleep for 5 s' # dbg
148 time.sleep(5.0)
148 time.sleep(5.0)
149 elif used_time > 10.0:
149 elif used_time > 10.0:
150 # print 'Sleep for 1 s' # dbg
150 # print 'Sleep for 1 s' # dbg
151 time.sleep(1.0)
151 time.sleep(1.0)
152 elif used_time > 0.1:
152 elif used_time > 0.1:
153 # Few GUI events coming in, so we can sleep longer
153 # Few GUI events coming in, so we can sleep longer
154 # print 'Sleep for 0.05 s' # dbg
154 # print 'Sleep for 0.05 s' # dbg
155 time.sleep(0.05)
155 time.sleep(0.05)
156 else:
156 else:
157 # Many GUI events coming in, so sleep only very little
157 # Many GUI events coming in, so sleep only very little
158 time.sleep(0.001)
158 time.sleep(0.001)
159 del ea
159 del ea
160 except KeyboardInterrupt:
160 except KeyboardInterrupt:
161 pass
161 pass
162 return 0
162 return 0
163
163
164 # This is our default implementation
164 if sys.platform == 'darwin':
165 inputhook_wx = inputhook_wx3
165 # On OSX, evtloop.Pending() always returns True, regardless of there being
166 # any events pending. As such we can't use implementations 1 or 3 of the
167 # inputhook as those depend on a pending/dispatch loop.
168 inputhook_wx = inputhook_wx2
169 else:
170 # This is our default implementation
171 inputhook_wx = inputhook_wx3
General Comments 0
You need to be logged in to leave comments. Login now