##// END OF EJS Templates
darker
darker

File last commit:

r25820:e0030b76
r26626:4fd46973
Show More
wx.py
219 lines | 7.0 KiB | text/x-python | PythonLexer
Min ho Kim
Fixed typos
r25167 """Enable wxPython to be used interactively in prompt_toolkit
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 """
import sys
import signal
import time
from timeit import default_timer as clock
import wx
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213 def ignore_keyboardinterrupts(func):
Paul McCarthy
RF: Clean up wxphoenix input hook.
r25214 """Decorator which causes KeyboardInterrupt exceptions to be ignored during
execution of the decorated function.
This is used by the inputhook functions to handle the event where the user
presses CTRL+C while IPython is idle, and the inputhook loop is running. In
this case, we want to ignore interrupts.
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213 """
def wrapper(*args, **kwargs):
try:
func(*args, **kwargs)
except KeyboardInterrupt:
pass
return wrapper
@ignore_keyboardinterrupts
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 def inputhook_wx1(context):
"""Run the wx event loop by processing pending events only.
This approach seems to work, but its performance is not great as it
relies on having PyOS_InputHook called regularly.
"""
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213 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
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 return 0
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 class EventLoopTimer(wx.Timer):
def __init__(self, func):
self.func = func
wx.Timer.__init__(self)
def Notify(self):
self.func()
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 class EventLoopRunner(object):
def Run(self, time, input_is_ready):
self.input_is_ready = input_is_ready
self.evtloop = wx.EventLoop()
self.timer = EventLoopTimer(self.check_stdin)
self.timer.Start(time)
self.evtloop.Run()
def check_stdin(self):
if self.input_is_ready():
self.timer.Stop()
self.evtloop.Exit()
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213
@ignore_keyboardinterrupts
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 def inputhook_wx2(context):
"""Run the wx event loop, polling for stdin.
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.
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
setting of 10 gives good keyboard response. We can shorten it further,
but eventually performance would suffer from calling select/kbhit too
often.
"""
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213 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
input_is_ready=context.input_is_ready)
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 return 0
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213
@ignore_keyboardinterrupts
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 def inputhook_wx3(context):
"""Run the wx event loop by processing pending events only.
This is like inputhook_wx1, but it keeps processing pending events
until stdin is ready. After processing all pending events, a call to
time.sleep is inserted. This is needed, otherwise, CPU usage is at 100%.
This sleep time should be tuned though for best performance.
"""
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213 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 context.input_is_ready():
while evtloop.Pending():
t = clock()
evtloop.Dispatch()
app.ProcessIdle()
# We need to sleep at this point to keep the idle CPU load
# low. However, if sleep to long, GUI response is poor. As
# 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 > 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
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 return 0
Paul McCarthy
ENH: Experimental input hook for wxpython phoenix
r25210
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213 @ignore_keyboardinterrupts
Paul McCarthy
ENH: Experimental input hook for wxpython phoenix
r25210 def inputhook_wxphoenix(context):
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 """Run the wx event loop until the user provides more input.
Paul McCarthy
ENH: Experimental input hook for wxpython phoenix
r25210
Paul McCarthy
DOC: More info in docstring.
r25216 This input hook is suitable for use with wxPython >= 4 (a.k.a. Phoenix).
It uses the same approach to that used in
Paul McCarthy
DOC: Clarify method in docstring
r25215 ipykernel.eventloops.loop_wx. The wx.MainLoop is executed, and a wx.Timer
is used to periodically poll the context for input. As soon as input is
ready, the wx.MainLoop is stopped.
Paul McCarthy
ENH: Experimental input hook for wxpython phoenix
r25210 """
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 app = wx.GetApp()
if app is None:
return
Paul McCarthy
RF: Clean up wxphoenix input hook.
r25214 if context.input_is_ready():
return
assert wx.IsMainThread()
# Wx uses milliseconds
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 poll_interval = 100
Paul McCarthy
RF: Clean up wxphoenix input hook.
r25214 # Use a wx.Timer to periodically check whether input is ready - as soon as
# it is, we exit the main loop
Paul McCarthy
BF: Make sure to stop timer, otherwise poll function may continue to get called...
r25820 timer = wx.Timer()
Paul McCarthy
RF: Clean up wxphoenix input hook.
r25214 def poll(ev):
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 if context.input_is_ready():
Paul McCarthy
BF: Make sure to stop timer, otherwise poll function may continue to get called...
r25820 timer.Stop()
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 app.ExitMainLoop()
Paul McCarthy
RF: Clean up wxphoenix input hook.
r25214 timer.Start(poll_interval)
timer.Bind(wx.EVT_TIMER, poll)
# 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.
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 if not callable(signal.getsignal(signal.SIGINT)):
signal.signal(signal.SIGINT, signal.default_int_handler)
Paul McCarthy
RF: Use SetExitOnFrameDelete instead of creating a frame
r25217 # The SetExitOnFrameDelete call allows us to run the wx mainloop without
# having a frame open.
app.SetExitOnFrameDelete(False)
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 app.MainLoop()
Paul McCarthy
RF: Factor out keyboard interrupt catch
r25213
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 # Get the major wx version number to figure out what input hook we should use.
major_version = 3
Paul McCarthy
RF: Clean up wxphoenix input hook.
r25214
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 try:
major_version = int(wx.__version__[0])
except Exception:
pass
# Use the phoenix hook on all platforms for wxpython >= 4
if major_version >= 4:
inputhook = inputhook_wxphoenix
# On OSX, evtloop.Pending() always returns True, regardless of there being
# any events pending. As such we can't use implementations 1 or 3 of the
# inputhook as those depend on a pending/dispatch loop.
elif sys.platform == 'darwin':
Thomas Kluyver
Add prompt_toolkit input hooks for wx
r21941 inputhook = inputhook_wx2
else:
Paul McCarthy
RF: Change wx strategy to that used by ipykernel.eventloops.loop_wx. Seems to...
r25212 inputhook = inputhook_wx3