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