##// END OF EJS Templates
Revert "Add debug statements to observe loop"...
Emilio Graff -
Show More
@@ -1,98 +1,86 b''
1 import sys
1 import sys
2 import os
2 import os
3 from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper
3 from IPython.external.qt_for_kernel import QtCore, QtGui, enum_helper
4 from IPython import get_ipython
4 from IPython import get_ipython
5
5
6 # If we create a QApplication, keep a reference to it so that it doesn't get
6 # If we create a QApplication, keep a reference to it so that it doesn't get
7 # garbage collected.
7 # garbage collected.
8 _appref = None
8 _appref = None
9 _already_warned = False
9 _already_warned = False
10
10
11
11
12 def _exec(obj):
12 def _exec(obj):
13 # exec on PyQt6, exec_ elsewhere.
13 # exec on PyQt6, exec_ elsewhere.
14 obj.exec() if hasattr(obj, "exec") else obj.exec_()
14 obj.exec() if hasattr(obj, "exec") else obj.exec_()
15
15
16
16
17 def _reclaim_excepthook():
17 def _reclaim_excepthook():
18 shell = get_ipython()
18 shell = get_ipython()
19 if shell is not None:
19 if shell is not None:
20 sys.excepthook = shell.excepthook
20 sys.excepthook = shell.excepthook
21
21
22
22
23 announced = 0
24
25
26 def inputhook(context):
23 def inputhook(context):
27 global _appref
24 global _appref
28 app = QtCore.QCoreApplication.instance()
25 app = QtCore.QCoreApplication.instance()
29 if not app:
26 if not app:
30 if sys.platform == 'linux':
27 if sys.platform == 'linux':
31 if not os.environ.get('DISPLAY') \
28 if not os.environ.get('DISPLAY') \
32 and not os.environ.get('WAYLAND_DISPLAY'):
29 and not os.environ.get('WAYLAND_DISPLAY'):
33 import warnings
30 import warnings
34 global _already_warned
31 global _already_warned
35 if not _already_warned:
32 if not _already_warned:
36 _already_warned = True
33 _already_warned = True
37 warnings.warn(
34 warnings.warn(
38 'The DISPLAY or WAYLAND_DISPLAY environment variable is '
35 'The DISPLAY or WAYLAND_DISPLAY environment variable is '
39 'not set or empty and Qt5 requires this environment '
36 'not set or empty and Qt5 requires this environment '
40 'variable. Deactivate Qt5 code.'
37 'variable. Deactivate Qt5 code.'
41 )
38 )
42 return
39 return
43 try:
40 try:
44 QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
41 QtCore.QApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling)
45 except AttributeError: # Only for Qt>=5.6, <6.
42 except AttributeError: # Only for Qt>=5.6, <6.
46 pass
43 pass
47 try:
44 try:
48 QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy(
45 QtCore.QApplication.setHighDpiScaleFactorRoundingPolicy(
49 QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
46 QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
50 )
47 )
51 except AttributeError: # Only for Qt>=5.14.
48 except AttributeError: # Only for Qt>=5.14.
52 pass
49 pass
53 _appref = app = QtGui.QApplication([" "])
50 _appref = app = QtGui.QApplication([" "])
54
51
55 # "reclaim" IPython sys.excepthook after event loop starts
52 # "reclaim" IPython sys.excepthook after event loop starts
56 # without this, it defaults back to BaseIPythonApplication.excepthook
53 # without this, it defaults back to BaseIPythonApplication.excepthook
57 # and exceptions in the Qt event loop are rendered without traceback
54 # and exceptions in the Qt event loop are rendered without traceback
58 # formatting and look like "bug in IPython".
55 # formatting and look like "bug in IPython".
59 QtCore.QTimer.singleShot(0, _reclaim_excepthook)
56 QtCore.QTimer.singleShot(0, _reclaim_excepthook)
60
57
61 event_loop = QtCore.QEventLoop(app)
58 event_loop = QtCore.QEventLoop(app)
62
59
63 global announced
64 if announced == 0:
65 print(f"`inputhook` running Qt {QtCore.qVersion()} event loop.")
66 announced += 1
67 elif announced == 10:
68 announced = 0
69 else:
70 announced += 1
71
72 if sys.platform == 'win32':
60 if sys.platform == 'win32':
73 # The QSocketNotifier method doesn't appear to work on Windows.
61 # The QSocketNotifier method doesn't appear to work on Windows.
74 # Use polling instead.
62 # Use polling instead.
75 timer = QtCore.QTimer()
63 timer = QtCore.QTimer()
76 timer.timeout.connect(event_loop.quit)
64 timer.timeout.connect(event_loop.quit)
77 while not context.input_is_ready():
65 while not context.input_is_ready():
78 # NOTE: run the event loop, and after 50 ms, call `quit` to exit it.
66 # NOTE: run the event loop, and after 50 ms, call `quit` to exit it.
79 timer.start(50) # 50 ms
67 timer.start(50) # 50 ms
80 _exec(event_loop)
68 _exec(event_loop)
81 timer.stop()
69 timer.stop()
82 else:
70 else:
83 # On POSIX platforms, we can use a file descriptor to quit the event
71 # On POSIX platforms, we can use a file descriptor to quit the event
84 # loop when there is input ready to read.
72 # loop when there is input ready to read.
85 notifier = QtCore.QSocketNotifier(
73 notifier = QtCore.QSocketNotifier(
86 context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read
74 context.fileno(), enum_helper("QtCore.QSocketNotifier.Type").Read
87 )
75 )
88 try:
76 try:
89 # connect the callback we care about before we turn it on
77 # connect the callback we care about before we turn it on
90 # lambda is necessary as PyQT inspect the function signature to know
78 # lambda is necessary as PyQT inspect the function signature to know
91 # what arguments to pass to. See https://github.com/ipython/ipython/pull/12355
79 # what arguments to pass to. See https://github.com/ipython/ipython/pull/12355
92 notifier.activated.connect(lambda: event_loop.exit())
80 notifier.activated.connect(lambda: event_loop.exit())
93 notifier.setEnabled(True)
81 notifier.setEnabled(True)
94 # only start the event loop we are not already flipped
82 # only start the event loop we are not already flipped
95 if not context.input_is_ready():
83 if not context.input_is_ready():
96 _exec(event_loop)
84 _exec(event_loop)
97 finally:
85 finally:
98 notifier.setEnabled(False)
86 notifier.setEnabled(False)
General Comments 0
You need to be logged in to leave comments. Login now