1
+ import contextlib
1
2
import os
2
3
import signal
3
4
import socket
@@ -106,6 +107,13 @@ def resize(self, width, height):
106
107
ResizeEvent ("resize_event" , self )._process ()
107
108
self .draw_idle ()
108
109
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
+
109
117
110
118
class NavigationToolbar2Mac (_macosx .NavigationToolbar2 , NavigationToolbar2 ):
111
119
@@ -171,34 +179,8 @@ def start_main_loop(cls):
171
179
# Set up a SIGINT handler to allow terminating a plot via CTRL-C.
172
180
# The logic is largely copied from qt_compat._maybe_allow_interrupt; see its
173
181
# 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 ():
194
183
_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 )
202
184
203
185
def show (self ):
204
186
if not self ._shown :
@@ -208,6 +190,45 @@ def show(self):
208
190
self ._raise ()
209
191
210
192
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
+
211
232
@_Backend .export
212
233
class _BackendMac (_Backend ):
213
234
FigureCanvas = FigureCanvasMac
0 commit comments