Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 276e928

Browse files
ksundenmeeseeksmachine
authored andcommitted
Backport PR #27221: FIX: Enable interrupts on macosx event loops
1 parent 23ec48d commit 276e928

File tree

2 files changed

+51
-30
lines changed

2 files changed

+51
-30
lines changed

lib/matplotlib/backends/backend_macosx.py

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import os
23
import signal
34
import socket
@@ -106,6 +107,13 @@ def resize(self, width, height):
106107
ResizeEvent("resize_event", self)._process()
107108
self.draw_idle()
108109

110+
def start_event_loop(self, timeout=0):
111+
# docstring inherited
112+
with _maybe_allow_interrupt():
113+
# Call the objc implementation of the event loop after
114+
# setting up the interrupt handling
115+
self._start_event_loop(timeout=timeout)
116+
109117

110118
class NavigationToolbar2Mac(_macosx.NavigationToolbar2, NavigationToolbar2):
111119

@@ -171,34 +179,8 @@ def start_main_loop(cls):
171179
# Set up a SIGINT handler to allow terminating a plot via CTRL-C.
172180
# The logic is largely copied from qt_compat._maybe_allow_interrupt; see its
173181
# docstring for details. Parts are implemented by wake_on_fd_write in ObjC.
174-
175-
old_sigint_handler = signal.getsignal(signal.SIGINT)
176-
if old_sigint_handler in (None, signal.SIG_IGN, signal.SIG_DFL):
177-
_macosx.show()
178-
return
179-
180-
handler_args = None
181-
wsock, rsock = socket.socketpair()
182-
wsock.setblocking(False)
183-
rsock.setblocking(False)
184-
old_wakeup_fd = signal.set_wakeup_fd(wsock.fileno())
185-
_macosx.wake_on_fd_write(rsock.fileno())
186-
187-
def handle(*args):
188-
nonlocal handler_args
189-
handler_args = args
190-
_macosx.stop()
191-
192-
signal.signal(signal.SIGINT, handle)
193-
try:
182+
with _maybe_allow_interrupt():
194183
_macosx.show()
195-
finally:
196-
wsock.close()
197-
rsock.close()
198-
signal.set_wakeup_fd(old_wakeup_fd)
199-
signal.signal(signal.SIGINT, old_sigint_handler)
200-
if handler_args is not None:
201-
old_sigint_handler(*handler_args)
202184

203185
def show(self):
204186
if not self._shown:
@@ -208,6 +190,45 @@ def show(self):
208190
self._raise()
209191

210192

193+
@contextlib.contextmanager
194+
def _maybe_allow_interrupt():
195+
"""
196+
This manager allows to terminate a plot by sending a SIGINT. It is
197+
necessary because the running backend prevents Python interpreter to
198+
run and process signals (i.e., to raise KeyboardInterrupt exception). To
199+
solve this one needs to somehow wake up the interpreter and make it close
200+
the plot window. The implementation is taken from qt_compat, see that
201+
docstring for a more detailed description.
202+
"""
203+
old_sigint_handler = signal.getsignal(signal.SIGINT)
204+
if old_sigint_handler in (None, signal.SIG_IGN, signal.SIG_DFL):
205+
yield
206+
return
207+
208+
handler_args = None
209+
wsock, rsock = socket.socketpair()
210+
wsock.setblocking(False)
211+
rsock.setblocking(False)
212+
old_wakeup_fd = signal.set_wakeup_fd(wsock.fileno())
213+
_macosx.wake_on_fd_write(rsock.fileno())
214+
215+
def handle(*args):
216+
nonlocal handler_args
217+
handler_args = args
218+
_macosx.stop()
219+
220+
signal.signal(signal.SIGINT, handle)
221+
try:
222+
yield
223+
finally:
224+
wsock.close()
225+
rsock.close()
226+
signal.set_wakeup_fd(old_wakeup_fd)
227+
signal.signal(signal.SIGINT, old_sigint_handler)
228+
if handler_args is not None:
229+
old_sigint_handler(*handler_args)
230+
231+
211232
@_Backend.export
212233
class _BackendMac(_Backend):
213234
FigureCanvas = FigureCanvasMac

src/_macosx.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ int mpl_check_modifier(
451451
}
452452

453453
static PyObject*
454-
FigureCanvas_start_event_loop(FigureCanvas* self, PyObject* args, PyObject* keywords)
454+
FigureCanvas__start_event_loop(FigureCanvas* self, PyObject* args, PyObject* keywords)
455455
{
456456
float timeout = 0.0;
457457

@@ -522,8 +522,8 @@ int mpl_check_modifier(
522522
(PyCFunction)FigureCanvas_remove_rubberband,
523523
METH_NOARGS,
524524
"Remove the current rubberband rectangle."},
525-
{"start_event_loop",
526-
(PyCFunction)FigureCanvas_start_event_loop,
525+
{"_start_event_loop",
526+
(PyCFunction)FigureCanvas__start_event_loop,
527527
METH_KEYWORDS | METH_VARARGS,
528528
NULL}, // docstring inherited
529529
{"stop_event_loop",

0 commit comments

Comments
 (0)