##// END OF EJS Templates
Workaround so only one CTRL-C is required for a new prompt in --gui=qt...
Siyu Zhang -
Show More
@@ -1,144 +1,158 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 19 from IPython.core.interactiveshell import InteractiveShell
20 20 from IPython.external.qt_for_kernel import QtCore, QtGui
21 21 from IPython.lib.inputhook import allow_CTRL_C, ignore_CTRL_C, stdin_ready
22 22
23 23 #-----------------------------------------------------------------------------
24 24 # Code
25 25 #-----------------------------------------------------------------------------
26 26
27 27 def create_inputhook_qt4(mgr, app=None):
28 28 """Create an input hook for running the Qt4 application event loop.
29 29
30 30 Parameters
31 31 ----------
32 32 mgr : an InputHookManager
33 33
34 34 app : Qt Application, optional.
35 35 Running application to use. If not given, we probe Qt for an
36 36 existing application object, and create a new one if none is found.
37 37
38 38 Returns
39 39 -------
40 40 A pair consisting of a Qt Application (either the one given or the
41 41 one found or created) and a inputhook.
42 42
43 43 Notes
44 44 -----
45 45 We use a custom input hook instead of PyQt4's default one, as it
46 46 interacts better with the readline packages (issue #481).
47 47
48 48 The inputhook function works in tandem with a 'pre_prompt_hook'
49 49 which automatically restores the hook as an inputhook in case the
50 50 latter has been temporarily disabled after having intercepted a
51 51 KeyboardInterrupt.
52 52 """
53 53
54 54 if app is None:
55 55 app = QtCore.QCoreApplication.instance()
56 56 if app is None:
57 57 app = QtGui.QApplication([" "])
58 58
59 59 # Re-use previously created inputhook if any
60 60 ip = InteractiveShell.instance()
61 61 if hasattr(ip, '_inputhook_qt4'):
62 62 return app, ip._inputhook_qt4
63 63
64 64 # Otherwise create the inputhook_qt4/preprompthook_qt4 pair of
65 65 # hooks (they both share the got_kbdint flag)
66 66
67 67 got_kbdint = [False]
68 68
69 69 def inputhook_qt4():
70 70 """PyOS_InputHook python hook for Qt4.
71 71
72 72 Process pending Qt events and if there's no pending keyboard
73 73 input, spend a short slice of time (50ms) running the Qt event
74 74 loop.
75 75
76 76 As a Python ctypes callback can't raise an exception, we catch
77 77 the KeyboardInterrupt and temporarily deactivate the hook,
78 78 which will let a *second* CTRL+C be processed normally and go
79 79 back to a clean prompt line.
80 80 """
81 81 try:
82 82 allow_CTRL_C()
83 83 app = QtCore.QCoreApplication.instance()
84 84 if not app: # shouldn't happen, but safer if it happens anyway...
85 85 return 0
86 86 app.processEvents(QtCore.QEventLoop.AllEvents, 300)
87 87 if not stdin_ready():
88 88 # Generally a program would run QCoreApplication::exec()
89 89 # from main() to enter and process the Qt event loop until
90 90 # quit() or exit() is called and the program terminates.
91 91 #
92 92 # For our input hook integration, we need to repeatedly
93 93 # enter and process the Qt event loop for only a short
94 94 # amount of time (say 50ms) to ensure that Python stays
95 95 # responsive to other user inputs.
96 96 #
97 97 # A naive approach would be to repeatedly call
98 98 # QCoreApplication::exec(), using a timer to quit after a
99 99 # short amount of time. Unfortunately, QCoreApplication
100 100 # emits an aboutToQuit signal before stopping, which has
101 101 # the undesirable effect of closing all modal windows.
102 102 #
103 103 # To work around this problem, we instead create a
104 104 # QEventLoop and call QEventLoop::exec(). Other than
105 105 # setting some state variables which do not seem to be
106 106 # used anywhere, the only thing QCoreApplication adds is
107 107 # the aboutToQuit signal which is precisely what we are
108 108 # trying to avoid.
109 109 timer = QtCore.QTimer()
110 110 event_loop = QtCore.QEventLoop()
111 111 timer.timeout.connect(event_loop.quit)
112 112 while not stdin_ready():
113 113 timer.start(50)
114 114 event_loop.exec_()
115 115 timer.stop()
116 116 except KeyboardInterrupt:
117 117 ignore_CTRL_C()
118 118 got_kbdint[0] = True
119 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
120 119 mgr.clear_inputhook()
120 import os, signal, time, threading
121 if(os.name == 'posix'):
122 pid = os.getpid()
123 print("^C")
124 thread = threading.Thread(
125 target = lambda:
126 ( time.sleep(.1),
127 os.kill(pid, signal.SIGINT) )
128 )
129
130 thread.start()
131 else:
132 print("\nKeyboardInterrupt - Ctrl-C again for new prompt")
133
134
121 135 except: # NO exceptions are allowed to escape from a ctypes callback
122 136 ignore_CTRL_C()
123 137 from traceback import print_exc
124 138 print_exc()
125 139 print("Got exception from inputhook_qt4, unregistering.")
126 140 mgr.clear_inputhook()
127 141 finally:
128 142 allow_CTRL_C()
129 143 return 0
130 144
131 145 def preprompthook_qt4(ishell):
132 146 """'pre_prompt_hook' used to restore the Qt4 input hook
133 147
134 148 (in case the latter was temporarily deactivated after a
135 149 CTRL+C)
136 150 """
137 151 if got_kbdint[0]:
138 152 mgr.set_inputhook(inputhook_qt4)
139 153 got_kbdint[0] = False
140 154
141 155 ip._inputhook_qt4 = inputhook_qt4
142 156 ip.set_hook('pre_prompt_hook', preprompthook_qt4)
143 157
144 158 return app, inputhook_qt4
General Comments 0
You need to be logged in to leave comments. Login now