Skip to content

Commit f1e925f

Browse files
committed
Add private API retrieving the current event loop and backend GUI info.
Work towards implementation of backend switching. Note that the API is kept private for now as the lack of extensibility is a bit unsatisfying; perhaps we'll figure out a better way to do it if other custom implementers (e.g., code editors?) get interested in looking into it. The `required_event_loop` variable could be made private for now, or not...
1 parent 65158f9 commit f1e925f

File tree

7 files changed

+65
-1
lines changed

7 files changed

+65
-1
lines changed

lib/matplotlib/backends/__init__.py

+45
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import importlib
22
import logging
3+
import os
4+
import sys
35
import traceback
46

57
import matplotlib
@@ -14,6 +16,49 @@
1416
if not line.startswith(' File "<frozen importlib._bootstrap'))
1517

1618

19+
def _get_current_event_loop():
20+
"""Return the currently running event loop if any, or "headless", or None.
21+
"headless" indicates that no event loop can be started.
22+
Returns
23+
-------
24+
Optional[str]
25+
One of the following values: "qt5", "qt4", "gtk3", "wx", "tk",
26+
"macosx", "headless", ``None``.
27+
"""
28+
QtWidgets = (sys.modules.get("PyQt5.QtWidgets")
29+
or sys.modules.get("PySide2.QtWidgets"))
30+
if QtWidgets and QtWidgets.QApplication.instance():
31+
return "qt5"
32+
QtGui = (sys.modules.get("PyQt4.QtGui")
33+
or sys.modules.get("PySide.QtGui"))
34+
if QtGui and QtGui.QApplication.instance():
35+
return "qt4"
36+
Gtk = (sys.modules.get("gi.repository.Gtk")
37+
or sys.modules.get("pgi.repository.Gtk"))
38+
if Gtk and Gtk.main_level():
39+
return "gtk3"
40+
wx = sys.modules.get("wx")
41+
if wx and wx.GetApp():
42+
return "wx"
43+
tkinter = sys.modules.get("tkinter")
44+
if tkinter and any(frame.f_code.co_filename == tkinter.__file__
45+
and frame.f_code.co_name == "mainloop"
46+
for frame in sys._current_frames().values()):
47+
return "tk"
48+
try:
49+
from matplotlib.backends import _macosx
50+
except ImportError:
51+
pass
52+
else:
53+
# Note that the NSApp event loop is also running when a non-native
54+
# toolkit (e.g. Qt5) is active, but in that case we want to report the
55+
# other toolkit; thus, this check comes after the other toolkits.
56+
if _macosx.event_loop_is_running():
57+
return "macosx"
58+
if sys.platform.startswith("linux") and not os.environ.get("DISPLAY"):
59+
return "headless"
60+
61+
1762
def pylab_setup(name=None):
1863
"""
1964
Return new_figure_manager, draw_if_interactive and show for pyplot.

lib/matplotlib/backends/_backend_tk.py

+1
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,7 @@ def trigger(self, *args):
994994

995995
@_Backend.export
996996
class _BackendTk(_Backend):
997+
required_event_loop = "tk"
997998
FigureManager = FigureManagerTk
998999

9991000
@classmethod

lib/matplotlib/backends/backend_gtk3.py

+1
Original file line numberDiff line numberDiff line change
@@ -971,6 +971,7 @@ def error_msg_gtk(msg, parent=None):
971971

972972
@_Backend.export
973973
class _BackendGTK3(_Backend):
974+
required_event_loop = "gtk3"
974975
FigureCanvas = FigureCanvasGTK3
975976
FigureManager = FigureManagerGTK3
976977

lib/matplotlib/backends/backend_qt4.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77

88
@_BackendQT5.export
99
class _BackendQT4(_BackendQT5):
10-
pass
10+
required_event_loop = "qt4"

lib/matplotlib/backends/backend_qt5.py

+1
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,7 @@ def exception_handler(type, value, tb):
11091109

11101110
@_Backend.export
11111111
class _BackendQT5(_Backend):
1112+
required_event_loop = "qt5"
11121113
FigureCanvas = FigureCanvasQT
11131114
FigureManager = FigureManagerQT
11141115

lib/matplotlib/backends/backend_wx.py

+1
Original file line numberDiff line numberDiff line change
@@ -1930,6 +1930,7 @@ def OnPrintPage(self, page):
19301930

19311931
@_Backend.export
19321932
class _BackendWx(_Backend):
1933+
required_event_loop = "wx"
19331934
FigureCanvas = FigureCanvasWx
19341935
FigureManager = FigureManagerWx
19351936
_frame_class = FigureFrameWx

src/_macosx.m

+15
Original file line numberDiff line numberDiff line change
@@ -2789,6 +2789,16 @@ - (int)index
27892789
}
27902790
@end
27912791

2792+
static PyObject*
2793+
event_loop_is_running(PyObject* self)
2794+
{
2795+
if ([NSApp isRunning]) {
2796+
Py_RETURN_TRUE;
2797+
} else {
2798+
Py_RETURN_FALSE;
2799+
}
2800+
}
2801+
27922802
static PyObject*
27932803
show(PyObject* self)
27942804
{
@@ -3038,6 +3048,11 @@ static bool verify_framework(void)
30383048
}
30393049

30403050
static struct PyMethodDef methods[] = {
3051+
{"event_loop_is_running",
3052+
(PyCFunction)event_loop_is_running,
3053+
METH_NOARGS,
3054+
"Return whether the NSApp main event loop is currently running."
3055+
},
30413056
{"show",
30423057
(PyCFunction)show,
30433058
METH_NOARGS,

0 commit comments

Comments
 (0)