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