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