Skip to content

Commit f9c06a4

Browse files
authored
Merge pull request #13027 from joelfrederico/qt5-reset-signals
Qt5 reset signals after non-interactive plotting
2 parents cc5c188 + 969782e commit f9c06a4

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

lib/matplotlib/backends/backend_qt5.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,6 +1124,11 @@ def trigger_manager_draw(manager):
11241124

11251125
@staticmethod
11261126
def mainloop():
1127-
# allow KeyboardInterrupt exceptions to close the plot window.
1127+
old_signal = signal.getsignal(signal.SIGINT)
1128+
# allow SIGINT exceptions to close the plot window.
11281129
signal.signal(signal.SIGINT, signal.SIG_DFL)
1129-
qApp.exec_()
1130+
try:
1131+
qApp.exec_()
1132+
finally:
1133+
# reset the SIGINT exception handler
1134+
signal.signal(signal.SIGINT, old_signal)

lib/matplotlib/tests/test_backend_qt.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,53 @@ def test_fig_close(backend):
114114
assert init_figs == Gcf.figs
115115

116116

117+
@pytest.mark.backend('Qt5Agg')
118+
def test_fig_signals(qt_module):
119+
# Create a figure
120+
fig = plt.figure()
121+
122+
# Access QtCore
123+
QtCore = qt_module[0]
124+
125+
# Access signals
126+
import signal
127+
event_loop_signal = None
128+
129+
# Callback to fire during event loop: save SIGINT handler, then exit
130+
def fire_signal_and_quit():
131+
# Save event loop signal
132+
nonlocal event_loop_signal
133+
event_loop_signal = signal.getsignal(signal.SIGINT)
134+
135+
# Request event loop exit
136+
QtCore.QCoreApplication.exit()
137+
138+
# Timer to exit event loop
139+
QtCore.QTimer.singleShot(0, fire_signal_and_quit)
140+
141+
# Save original SIGINT handler
142+
original_signal = signal.getsignal(signal.SIGINT)
143+
144+
# Use our own SIGINT handler to be 100% sure this is working
145+
def CustomHandler(signum, frame):
146+
pass
147+
148+
signal.signal(signal.SIGINT, CustomHandler)
149+
150+
# mainloop() sets SIGINT, starts Qt event loop (which trigers timer and
151+
# exits) and then mainloop() resets SIGINT
152+
matplotlib.backends.backend_qt5._BackendQT5.mainloop()
153+
154+
# Assert: signal handler during loop execution is signal.SIG_DFL
155+
assert event_loop_signal == signal.SIG_DFL
156+
157+
# Assert: current signal handler is the same as the one we set before
158+
assert CustomHandler == signal.getsignal(signal.SIGINT)
159+
160+
# Reset SIGINT handler to what it was before the test
161+
signal.signal(signal.SIGINT, original_signal)
162+
163+
117164
@pytest.mark.parametrize(
118165
'qt_key, qt_mods, answer',
119166
[

0 commit comments

Comments
 (0)