##// END OF EJS Templates
Merge pull request #3219 from siyuz/ctrl-c_clear_line...
Fernando Perez -
r10471:55129be9 merge
parent child Browse files
Show More
@@ -1,144 +1,181 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Qt4's inputhook support function
4 4
5 5 Author: Christian Boos
6 6 """
7 7
8 8 #-----------------------------------------------------------------------------
9 9 # Copyright (C) 2011 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is in
12 12 # the file COPYING, distributed as part of this software.
13 13 #-----------------------------------------------------------------------------
14 14
15 15 #-----------------------------------------------------------------------------
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 import os
20 import signal
21 import time
22 import threading
23
19 24 from IPython.core.interactiveshell import InteractiveShell
20 25 from IPython.external.qt_for_kernel import QtCore, QtGui
21 26 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
22 27
23 28 #-----------------------------------------------------------------------------
29 # Module Globals
30 #-----------------------------------------------------------------------------
31
32 got_kbdint = False
33 sigint_timer = None
34
35 #-----------------------------------------------------------------------------
24 36 # Code
25 37 #-----------------------------------------------------------------------------
26 38
27 39 def create_inputhook_qt4(mgr, app=None):
28 40 """Create an input hook for running the Qt4 application event loop.
29 41
30 42 Parameters
31 43 ----------
32 44 mgr : an InputHookManager
33 45
34 46 app : Qt Application, optional.
35 47 Running application to use. If not given, we probe Qt for an
36 48 existing application object, and create a new one if none is found.
37 49
38 50 Returns
39 51 -------
40 52 A pair consisting of a Qt Application (either the one given or the
41 53 one found or created) and a inputhook.
42 54
43 55 Notes
44 56 -----
45 57 We use a custom input hook instead of PyQt4's default one, as it
46 58 interacts better with the readline packages (issue #481).
47 59
48 60 The inputhook function works in tandem with a 'pre_prompt_hook'
49 61 which automatically restores the hook as an inputhook in case the
50 62 latter has been temporarily disabled after having intercepted a
51 63 KeyboardInterrupt.
52 64 """
53 65
54 66 if app is None:
55 67 app = QtCore.QCoreApplication.instance()
56 68 if app is None:
57 69 app = QtGui.QApplication([" "])
58 70
59 71 # Re-use previously created inputhook if any
60 72 ip = InteractiveShell.instance()
61 73 if hasattr(ip, '_inputhook_qt4'):
62 74 return app, ip._inputhook_qt4
63 75
64 76 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
65 77 # hooks (they both share the got_kbdint flag)
66 78
67 got_kbdint = [False]
68
69 79 def inputhook_qt4():
70 80 """PyOS_InputHook python hook for Qt4.
71 81
72 82 Process pending Qt events and if there's no pending keyboard
73 83 input, spend a short slice of time (50ms) running the Qt event
74 84 loop.
75 85
76 86 As a Python ctypes callback can't raise an exception, we catch
77 87 the KeyboardInterrupt and temporarily deactivate the hook,
78 88 which will let a *second* CTRL+C be processed normally and go
79 89 back to a clean prompt line.
80 90 """
81 91 try:
82 92 allow_CTRL_C()
83 93 app = QtCore.QCoreApplication.instance()
84 94 if not app: # shouldn't happen, but safer if it happens anyway...
85 95 return 0
86 96 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
87 97 if not stdin_ready():
88 98 # Generally a program would run QCoreApplication::exec()
89 99 # from main() to enter and process the Qt event loop until
90 100 # quit() or exit() is called and the program terminates.
91 101 #
92 102 # For our input hook integration, we need to repeatedly
93 103 # enter and process the Qt event loop for only a short
94 104 # amount of time (say 50ms) to ensure that Python stays
95 105 # responsive to other user inputs.
96 106 #
97 107 # A naive approach would be to repeatedly call
98 108 # QCoreApplication::exec(), using a timer to quit after a
99 109 # short amount of time. Unfortunately, QCoreApplication
100 110 # emits an aboutToQuit signal before stopping, which has
101 111 # the undesirable effect of closing all modal windows.
102 112 #
103 113 # To work around this problem, we instead create a
104 114 # QEventLoop and call QEventLoop::exec(). Other than
105 115 # setting some state variables which do not seem to be
106 116 # used anywhere, the only thing QCoreApplication adds is
107 117 # the aboutToQuit signal which is precisely what we are
108 118 # trying to avoid.
109 119 timer = QtCore.QTimer()
110 120 event_loop = QtCore.QEventLoop()
111 121 timer.timeout.connect(event_loop.quit)
112 122 while not stdin_ready():
113 123 timer.start(50)
114 124 event_loop.exec_()
115 125 timer.stop()
116 126 except KeyboardInterrupt:
127 global got_kbdint, sigint_timer
128
117 129 ignore_CTRL_C()
118 got_kbdint[0] = True
119 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
130 got_kbdint = True
120 131 mgr.clear_inputhook()
132
133 # This generates a second SIGINT so the user doesn't have to
134 # press CTRL+C twice to get a clean prompt.
135 #
136 # Since we can't catch the resulting KeyboardInterrupt here
137 # (because this is a ctypes callback), we use a timer to
138 # generate the SIGINT after we leave this callback.
139 #
140 # Unfortunately this doesn't work on Windows (SIGINT kills
141 # Python and CTRL_C_EVENT doesn't work).
142 if(os.name == 'posix'):
143 pid = os.getpid()
144 if(not sigint_timer):
145 sigint_timer = threading.Timer(.01, os.kill,
146 args=[pid, signal.SIGINT] )
147 sigint_timer.start()
148 else:
149 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
150
151
121 152 except: # NO exceptions are allowed to escape from a ctypes callback
122 153 ignore_CTRL_C()
123 154 from traceback import print_exc
124 155 print_exc()
125 156 print("Got exception from inputhook_qt4, unregistering.")
126 157 mgr.clear_inputhook()
127 158 finally:
128 159 allow_CTRL_C()
129 160 return 0
130 161
131 162 def preprompthook_qt4(ishell):
132 163 """'pre_prompt_hook' used to restore the Qt4 input hook
133 164
134 165 (in case the latter was temporarily deactivated after a
135 166 CTRL+C)
136 167 """
137 if got_kbdint[0]:
168 global got_kbdint, sigint_timer
169
170 if(sigint_timer):
171 sigint_timer.cancel()
172 sigint_timer = None
173
174 if got_kbdint:
138 175 mgr.set_inputhook(inputhook_qt4)
139 got_kbdint[0] = False
176 got_kbdint = False
140 177
141 178 ip._inputhook_qt4 = inputhook_qt4
142 179 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
143 180
144 181 return app, inputhook_qt4
General Comments 0
You need to be logged in to leave comments. Login now