inputhookwx.py
178 lines
| 6.1 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2066 | # encoding: utf-8 | ||
""" | ||||
Enable wxPython to be used interacive by setting PyOS_InputHook. | ||||
Authors: Robin Dunn, Brian Granger, Ondrej Certik | ||||
""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2008-2009 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
import os | ||||
Brian Granger
|
r2208 | import signal | ||
Brian Granger
|
r2066 | import sys | ||
import time | ||||
from timeit import default_timer as clock | ||||
import wx | ||||
if os.name == 'posix': | ||||
import select | ||||
elif sys.platform == 'win32': | ||||
import msvcrt | ||||
#----------------------------------------------------------------------------- | ||||
# Code | ||||
#----------------------------------------------------------------------------- | ||||
def stdin_ready(): | ||||
if os.name == 'posix': | ||||
infds, outfds, erfds = select.select([sys.stdin],[],[],0) | ||||
if infds: | ||||
return True | ||||
else: | ||||
return False | ||||
elif sys.platform == 'win32': | ||||
return msvcrt.kbhit() | ||||
def inputhook_wx1(): | ||||
"""Run the wx event loop by processing pending events only. | ||||
Bernardo B. Marques
|
r4872 | |||
This approach seems to work, but its performance is not great as it | ||||
Brian Granger
|
r2066 | relies on having PyOS_InputHook called regularly. | ||
""" | ||||
Brian Granger
|
r2345 | try: | ||
app = wx.GetApp() | ||||
if app is not None: | ||||
assert wx.Thread_IsMain() | ||||
# Make a temporary event loop and process system events until | ||||
# there are no more waiting, then allow idle events (which | ||||
# will also deal with pending or posted wx events.) | ||||
evtloop = wx.EventLoop() | ||||
ea = wx.EventLoopActivator(evtloop) | ||||
while evtloop.Pending(): | ||||
evtloop.Dispatch() | ||||
app.ProcessIdle() | ||||
del ea | ||||
except KeyboardInterrupt: | ||||
pass | ||||
Brian Granger
|
r2066 | return 0 | ||
class EventLoopTimer(wx.Timer): | ||||
def __init__(self, func): | ||||
self.func = func | ||||
wx.Timer.__init__(self) | ||||
def Notify(self): | ||||
self.func() | ||||
class EventLoopRunner(object): | ||||
def Run(self, time): | ||||
self.evtloop = wx.EventLoop() | ||||
self.timer = EventLoopTimer(self.check_stdin) | ||||
self.timer.Start(time) | ||||
self.evtloop.Run() | ||||
def check_stdin(self): | ||||
if stdin_ready(): | ||||
self.timer.Stop() | ||||
self.evtloop.Exit() | ||||
def inputhook_wx2(): | ||||
"""Run the wx event loop, polling for stdin. | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2066 | This version runs the wx eventloop for an undetermined amount of time, | ||
during which it periodically checks to see if anything is ready on | ||||
stdin. If anything is ready on stdin, the event loop exits. | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2066 | The argument to elr.Run controls how often the event loop looks at stdin. | ||
This determines the responsiveness at the keyboard. A setting of 1000 | ||||
enables a user to type at most 1 char per second. I have found that a | ||||
Bernardo B. Marques
|
r4872 | setting of 10 gives good keyboard response. We can shorten it further, | ||
but eventually performance would suffer from calling select/kbhit too | ||||
Brian Granger
|
r2066 | often. | ||
""" | ||||
Brian Granger
|
r2345 | try: | ||
app = wx.GetApp() | ||||
if app is not None: | ||||
assert wx.Thread_IsMain() | ||||
elr = EventLoopRunner() | ||||
# As this time is made shorter, keyboard response improves, but idle | ||||
# CPU load goes up. 10 ms seems like a good compromise. | ||||
elr.Run(time=10) # CHANGE time here to control polling interval | ||||
except KeyboardInterrupt: | ||||
pass | ||||
Brian Granger
|
r2066 | return 0 | ||
def inputhook_wx3(): | ||||
"""Run the wx event loop by processing pending events only. | ||||
Bernardo B. Marques
|
r4872 | |||
Brian Granger
|
r2066 | This is like inputhook_wx1, but it keeps processing pending events | ||
Bernardo B. Marques
|
r4872 | until stdin is ready. After processing all pending events, a call to | ||
Brian Granger
|
r2066 | time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%. | ||
This sleep time should be tuned though for best performance. | ||||
""" | ||||
Brian Granger
|
r2345 | # We need to protect against a user pressing Control-C when IPython is | ||
# idle and this is running. We trap KeyboardInterrupt and pass. | ||||
try: | ||||
app = wx.GetApp() | ||||
if app is not None: | ||||
assert wx.Thread_IsMain() | ||||
# The import of wx on Linux sets the handler for signal.SIGINT | ||||
# to 0. This is a bug in wx or gtk. We fix by just setting it | ||||
# back to the Python default. | ||||
if not callable(signal.getsignal(signal.SIGINT)): | ||||
signal.signal(signal.SIGINT, signal.default_int_handler) | ||||
evtloop = wx.EventLoop() | ||||
ea = wx.EventLoopActivator(evtloop) | ||||
t = clock() | ||||
while not stdin_ready(): | ||||
while evtloop.Pending(): | ||||
t = clock() | ||||
evtloop.Dispatch() | ||||
app.ProcessIdle() | ||||
# We need to sleep at this point to keep the idle CPU load | ||||
Bernardo B. Marques
|
r4872 | # low. However, if sleep to long, GUI response is poor. As | ||
Brian Granger
|
r2345 | # a compromise, we watch how often GUI events are being processed | ||
# and switch between a short and long sleep time. Here are some | ||||
# stats useful in helping to tune this. | ||||
# time CPU load | ||||
# 0.001 13% | ||||
# 0.005 3% | ||||
# 0.01 1.5% | ||||
# 0.05 0.5% | ||||
used_time = clock() - t | ||||
if used_time > 5*60.0: | ||||
# print 'Sleep for 5 s' # dbg | ||||
time.sleep(5.0) | ||||
elif used_time > 10.0: | ||||
# print 'Sleep for 1 s' # dbg | ||||
time.sleep(1.0) | ||||
elif used_time > 0.1: | ||||
# Few GUI events coming in, so we can sleep longer | ||||
# print 'Sleep for 0.05 s' # dbg | ||||
time.sleep(0.05) | ||||
else: | ||||
# Many GUI events coming in, so sleep only very little | ||||
time.sleep(0.001) | ||||
del ea | ||||
except KeyboardInterrupt: | ||||
pass | ||||
Brian Granger
|
r2066 | return 0 | ||
# This is our default implementation | ||||
Fernando Perez
|
r2239 | inputhook_wx = inputhook_wx3 | ||