inputhookwx.py 5.9 KB

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