47
47
except TypeError as exc :
48
48
cursord = {} # deprecated in Matplotlib 3.5.
49
49
50
+ # Placeholder
51
+ _application = None
52
+
53
+
54
+ def _shutdown_application (app ):
55
+ # The application might prematurely shut down if Ctrl-C'd out of IPython,
56
+ # so close all windows.
57
+ for win in app .get_windows ():
58
+ win .destroy ()
59
+ # The PyGObject wrapper incorrectly thinks that None is not allowed, or we
60
+ # would call this:
61
+ # Gio.Application.set_default(None)
62
+ # Instead, we set this property and ignore default applications with it:
63
+ app ._created_by_matplotlib = True
64
+ global _application
65
+ _application = None
66
+
67
+
68
+ def _create_application ():
69
+ global _application
70
+
71
+ if _application is None :
72
+ app = Gio .Application .get_default ()
73
+ if app is None or getattr (app , '_created_by_matplotlib' ):
74
+ # display_is_valid returns False only if on Linux and neither X11
75
+ # nor Wayland display can be opened.
76
+ if not mpl ._c_internal_utils .display_is_valid ():
77
+ raise RuntimeError ('Invalid DISPLAY variable' )
78
+ _application = Gtk .Application .new ('org.matplotlib.Matplotlib3' ,
79
+ Gio .ApplicationFlags .NON_UNIQUE )
80
+ # The activate signal must be connected, but we don't care for
81
+ # handling it, since we don't do any remote processing.
82
+ _application .connect ('activate' , lambda * args , ** kwargs : None )
83
+ _application .connect ('shutdown' , _shutdown_application )
84
+ _application .register ()
85
+ cbook ._setup_new_guiapp ()
86
+ else :
87
+ _application = app
88
+
50
89
51
90
@functools .lru_cache ()
52
91
def _mpl_to_gtk_cursor (mpl_cursor ):
@@ -293,11 +332,9 @@ def idle_draw(*args):
293
332
294
333
def flush_events (self ):
295
334
# docstring inherited
296
- Gdk .threads_enter ()
297
- while Gtk .events_pending ():
298
- Gtk .main_iteration ()
299
- Gdk .flush ()
300
- Gdk .threads_leave ()
335
+ context = GLib .MainContext .default ()
336
+ while context .pending ():
337
+ context .iteration (True )
301
338
302
339
303
340
class FigureManagerGTK3 (FigureManagerBase ):
@@ -317,7 +354,9 @@ class FigureManagerGTK3(FigureManagerBase):
317
354
318
355
"""
319
356
def __init__ (self , canvas , num ):
357
+ _create_application ()
320
358
self .window = Gtk .Window ()
359
+ _application .add_window (self .window )
321
360
super ().__init__ (canvas , num )
322
361
323
362
self .window .set_wmclass ("matplotlib" , "Matplotlib" )
@@ -379,10 +418,6 @@ def destroy(self, *args):
379
418
if self .toolbar :
380
419
self .toolbar .destroy ()
381
420
382
- if (Gcf .get_num_fig_managers () == 0 and not mpl .is_interactive () and
383
- Gtk .main_level () >= 1 ):
384
- Gtk .main_quit ()
385
-
386
421
def show (self ):
387
422
# show the figure window
388
423
self .window .show ()
@@ -499,7 +534,8 @@ def set_cursor(self, cursor):
499
534
window = self .canvas .get_property ("window" )
500
535
if window is not None :
501
536
window .set_cursor (_mpl_to_gtk_cursor (cursor ))
502
- Gtk .main_iteration ()
537
+ context = GLib .MainContext .default ()
538
+ context .iteration (True )
503
539
504
540
def draw_rubberband (self , event , x0 , y0 , x1 , y1 ):
505
541
height = self .canvas .figure .bbox .height
@@ -826,6 +862,12 @@ class _BackendGTK3(_Backend):
826
862
827
863
@staticmethod
828
864
def mainloop ():
829
- if Gtk .main_level () == 0 :
830
- cbook ._setup_new_guiapp ()
831
- Gtk .main ()
865
+ global _application
866
+ if _application is None :
867
+ return
868
+
869
+ try :
870
+ _application .run () # Quits when all added windows close.
871
+ finally :
872
+ # Running after quit is undefined, so create a new one next time.
873
+ _application = None
0 commit comments