##// END OF EJS Templates
RF: Factor out keyboard interrupt catch
Paul McCarthy -
Show More
@@ -8,30 +8,41 b' from timeit import default_timer as clock'
8 import wx
8 import wx
9
9
10
10
11 def ignore_keyboardinterrupts(func):
12 """Decorator which causes KeyboardInterrupt exceptions to be
13 ignored during execution of the decorated function.
14 """
15 def wrapper(*args, **kwargs):
16 try:
17 func(*args, **kwargs)
18 except KeyboardInterrupt:
19 pass
20 return wrapper
21
22
23 @ignore_keyboardinterrupts
11 def inputhook_wx1(context):
24 def inputhook_wx1(context):
12 """Run the wx event loop by processing pending events only.
25 """Run the wx event loop by processing pending events only.
13
26
14 This approach seems to work, but its performance is not great as it
27 This approach seems to work, but its performance is not great as it
15 relies on having PyOS_InputHook called regularly.
28 relies on having PyOS_InputHook called regularly.
16 """
29 """
17 try:
30 app = wx.GetApp()
18 app = wx.GetApp()
31 if app is not None:
19 if app is not None:
32 assert wx.Thread_IsMain()
20 assert wx.Thread_IsMain()
33
21
34 # Make a temporary event loop and process system events until
22 # Make a temporary event loop and process system events until
35 # there are no more waiting, then allow idle events (which
23 # there are no more waiting, then allow idle events (which
36 # will also deal with pending or posted wx events.)
24 # will also deal with pending or posted wx events.)
37 evtloop = wx.EventLoop()
25 evtloop = wx.EventLoop()
38 ea = wx.EventLoopActivator(evtloop)
26 ea = wx.EventLoopActivator(evtloop)
39 while evtloop.Pending():
27 while evtloop.Pending():
40 evtloop.Dispatch()
28 evtloop.Dispatch()
41 app.ProcessIdle()
29 app.ProcessIdle()
42 del ea
30 del ea
31 except KeyboardInterrupt:
32 pass
33 return 0
43 return 0
34
44
45
35 class EventLoopTimer(wx.Timer):
46 class EventLoopTimer(wx.Timer):
36
47
37 def __init__(self, func):
48 def __init__(self, func):
@@ -41,6 +52,7 b' class EventLoopTimer(wx.Timer):'
41 def Notify(self):
52 def Notify(self):
42 self.func()
53 self.func()
43
54
55
44 class EventLoopRunner(object):
56 class EventLoopRunner(object):
45
57
46 def Run(self, time, input_is_ready):
58 def Run(self, time, input_is_ready):
@@ -55,6 +67,8 b' class EventLoopRunner(object):'
55 self.timer.Stop()
67 self.timer.Stop()
56 self.evtloop.Exit()
68 self.evtloop.Exit()
57
69
70
71 @ignore_keyboardinterrupts
58 def inputhook_wx2(context):
72 def inputhook_wx2(context):
59 """Run the wx event loop, polling for stdin.
73 """Run the wx event loop, polling for stdin.
60
74
@@ -69,19 +83,18 b' def inputhook_wx2(context):'
69 but eventually performance would suffer from calling select/kbhit too
83 but eventually performance would suffer from calling select/kbhit too
70 often.
84 often.
71 """
85 """
72 try:
86 app = wx.GetApp()
73 app = wx.GetApp()
87 if app is not None:
74 if app is not None:
88 assert wx.Thread_IsMain()
75 assert wx.Thread_IsMain()
89 elr = EventLoopRunner()
76 elr = EventLoopRunner()
90 # As this time is made shorter, keyboard response improves, but idle
77 # As this time is made shorter, keyboard response improves, but idle
91 # CPU load goes up. 10 ms seems like a good compromise.
78 # CPU load goes up. 10 ms seems like a good compromise.
92 elr.Run(time=10, # CHANGE time here to control polling interval
79 elr.Run(time=10, # CHANGE time here to control polling interval
93 input_is_ready=context.input_is_ready)
80 input_is_ready=context.input_is_ready)
81 except KeyboardInterrupt:
82 pass
83 return 0
94 return 0
84
95
96
97 @ignore_keyboardinterrupts
85 def inputhook_wx3(context):
98 def inputhook_wx3(context):
86 """Run the wx event loop by processing pending events only.
99 """Run the wx event loop by processing pending events only.
87
100
@@ -90,54 +103,50 b' def inputhook_wx3(context):'
90 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
103 time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
91 This sleep time should be tuned though for best performance.
104 This sleep time should be tuned though for best performance.
92 """
105 """
93 # We need to protect against a user pressing Control-C when IPython is
106 app = wx.GetApp()
94 # idle and this is running. We trap KeyboardInterrupt and pass.
107 if app is not None:
95 try:
108 assert wx.Thread_IsMain()
96 app = wx.GetApp()
109
97 if app is not None:
110 # The import of wx on Linux sets the handler for signal.SIGINT
98 assert wx.Thread_IsMain()
111 # to 0. This is a bug in wx or gtk. We fix by just setting it
99
112 # back to the Python default.
100 # The import of wx on Linux sets the handler for signal.SIGINT
113 if not callable(signal.getsignal(signal.SIGINT)):
101 # to 0. This is a bug in wx or gtk. We fix by just setting it
114 signal.signal(signal.SIGINT, signal.default_int_handler)
102 # back to the Python default.
115
103 if not callable(signal.getsignal(signal.SIGINT)):
116 evtloop = wx.EventLoop()
104 signal.signal(signal.SIGINT, signal.default_int_handler)
117 ea = wx.EventLoopActivator(evtloop)
105
118 t = clock()
106 evtloop = wx.EventLoop()
119 while not context.input_is_ready():
107 ea = wx.EventLoopActivator(evtloop)
120 while evtloop.Pending():
108 t = clock()
121 t = clock()
109 while not context.input_is_ready():
122 evtloop.Dispatch()
110 while evtloop.Pending():
123 app.ProcessIdle()
111 t = clock()
124 # We need to sleep at this point to keep the idle CPU load
112 evtloop.Dispatch()
125 # low. However, if sleep to long, GUI response is poor. As
113 app.ProcessIdle()
126 # a compromise, we watch how often GUI events are being processed
114 # We need to sleep at this point to keep the idle CPU load
127 # and switch between a short and long sleep time. Here are some
115 # low. However, if sleep to long, GUI response is poor. As
128 # stats useful in helping to tune this.
116 # a compromise, we watch how often GUI events are being processed
129 # time CPU load
117 # and switch between a short and long sleep time. Here are some
130 # 0.001 13%
118 # stats useful in helping to tune this.
131 # 0.005 3%
119 # time CPU load
132 # 0.01 1.5%
120 # 0.001 13%
133 # 0.05 0.5%
121 # 0.005 3%
134 used_time = clock() - t
122 # 0.01 1.5%
135 if used_time > 10.0:
123 # 0.05 0.5%
136 # print 'Sleep for 1 s' # dbg
124 used_time = clock() - t
137 time.sleep(1.0)
125 if used_time > 10.0:
138 elif used_time > 0.1:
126 # print 'Sleep for 1 s' # dbg
139 # Few GUI events coming in, so we can sleep longer
127 time.sleep(1.0)
140 # print 'Sleep for 0.05 s' # dbg
128 elif used_time > 0.1:
141 time.sleep(0.05)
129 # Few GUI events coming in, so we can sleep longer
142 else:
130 # print 'Sleep for 0.05 s' # dbg
143 # Many GUI events coming in, so sleep only very little
131 time.sleep(0.05)
144 time.sleep(0.001)
132 else:
145 del ea
133 # Many GUI events coming in, so sleep only very little
134 time.sleep(0.001)
135 del ea
136 except KeyboardInterrupt:
137 pass
138 return 0
146 return 0
139
147
140
148
149 @ignore_keyboardinterrupts
141 def inputhook_wxphoenix(context):
150 def inputhook_wxphoenix(context):
142 """Run the wx event loop until the user provides more input.
151 """Run the wx event loop until the user provides more input.
143
152
@@ -183,6 +192,7 b' def inputhook_wxphoenix(context):'
183
192
184 app.MainLoop()
193 app.MainLoop()
185
194
195
186 # Get the major wx version number to figure out what input hook we should use.
196 # Get the major wx version number to figure out what input hook we should use.
187 major_version = 3
197 major_version = 3
188 try:
198 try:
General Comments 0
You need to be logged in to leave comments. Login now