Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cd185ab

Browse filesBrowse files
authored
Merge pull request #23101 from anntzer/inheritable-backend-show
Move show() to somewhere naturally inheritable / document what pyplot expects from a backend.
2 parents 19d9340 + 86f26a0 commit cd185ab
Copy full SHA for cd185ab

File tree

Expand file treeCollapse file tree

11 files changed

+174
-90
lines changed
Filter options
Expand file treeCollapse file tree

11 files changed

+174
-90
lines changed

‎lib/matplotlib/backend_bases.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backend_bases.py
+55-3Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2848,6 +2848,53 @@ def create_with_canvas(cls, canvas_class, figure, num):
28482848
"""
28492849
return cls(canvas_class(figure), num)
28502850

2851+
@classmethod
2852+
def start_main_loop(cls):
2853+
"""
2854+
Start the main event loop.
2855+
2856+
This method is called by `.FigureManagerBase.pyplot_show`, which is the
2857+
implementation of `.pyplot.show`. To customize the behavior of
2858+
`.pyplot.show`, interactive backends should usually override
2859+
`~.FigureManagerBase.start_main_loop`; if more customized logic is
2860+
necessary, `~.FigureManagerBase.pyplot_show` can also be overridden.
2861+
"""
2862+
2863+
@classmethod
2864+
def pyplot_show(cls, *, block=None):
2865+
"""
2866+
Show all figures. This method is the implementation of `.pyplot.show`.
2867+
2868+
To customize the behavior of `.pyplot.show`, interactive backends
2869+
should usually override `~.FigureManagerBase.start_main_loop`; if more
2870+
customized logic is necessary, `~.FigureManagerBase.pyplot_show` can
2871+
also be overridden.
2872+
2873+
Parameters
2874+
----------
2875+
block : bool, optional
2876+
Whether to block by calling ``start_main_loop``. The default,
2877+
None, means to block if we are neither in IPython's ``%pylab`` mode
2878+
nor in ``interactive`` mode.
2879+
"""
2880+
managers = Gcf.get_all_fig_managers()
2881+
if not managers:
2882+
return
2883+
for manager in managers:
2884+
try:
2885+
manager.show() # Emits a warning for non-interactive backend.
2886+
except NonGuiException as exc:
2887+
_api.warn_external(str(exc))
2888+
if block is None:
2889+
# Hack: Are we in IPython's %pylab mode? In pylab mode, IPython
2890+
# (>= 0.10) tacks a _needmain attribute onto pyplot.show (always
2891+
# set to False).
2892+
ipython_pylab = hasattr(
2893+
getattr(sys.modules.get("pyplot"), "show", None), "_needmain")
2894+
block = not ipython_pylab and not is_interactive()
2895+
if block:
2896+
cls.start_main_loop()
2897+
28512898
def show(self):
28522899
"""
28532900
For GUI backends, show the figure window and redraw.
@@ -3526,7 +3573,12 @@ def new_figure_manager_given_figure(cls, num, figure):
35263573

35273574
@classmethod
35283575
def draw_if_interactive(cls):
3529-
if cls.mainloop is not None and is_interactive():
3576+
manager_class = cls.FigureCanvas.manager_class
3577+
# Interactive backends reimplement start_main_loop or pyplot_show.
3578+
backend_is_interactive = (
3579+
manager_class.start_main_loop != FigureManagerBase.start_main_loop
3580+
or manager_class.pyplot_show != FigureManagerBase.pyplot_show)
3581+
if backend_is_interactive and is_interactive():
35303582
manager = Gcf.get_active()
35313583
if manager:
35323584
manager.canvas.draw_idle()
@@ -3554,8 +3606,8 @@ def show(cls, *, block=None):
35543606
# Hack: Are we in IPython's %pylab mode? In pylab mode, IPython
35553607
# (>= 0.10) tacks a _needmain attribute onto pyplot.show (always
35563608
# set to False).
3557-
from matplotlib import pyplot
3558-
ipython_pylab = hasattr(pyplot.show, "_needmain")
3609+
ipython_pylab = hasattr(
3610+
getattr(sys.modules.get("pyplot"), "show", None), "_needmain")
35593611
block = not ipython_pylab and not is_interactive()
35603612
if block:
35613613
cls.mainloop()

‎lib/matplotlib/backends/_backend_gtk.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/_backend_gtk.py
+26-20Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
from matplotlib import _api, backend_tools, cbook
1010
from matplotlib._pylab_helpers import Gcf
1111
from matplotlib.backend_bases import (
12-
_Backend, FigureManagerBase, NavigationToolbar2, TimerBase)
12+
_Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2,
13+
TimerBase)
1314
from matplotlib.backend_tools import Cursors
1415

1516
import gi
@@ -113,6 +114,10 @@ def _on_timer(self):
113114
return False
114115

115116

117+
class _FigureCanvasGTK(FigureCanvasBase):
118+
_timer_cls = TimerGTK
119+
120+
116121
class _FigureManagerGTK(FigureManagerBase):
117122
"""
118123
Attributes
@@ -192,6 +197,25 @@ def destroy(self, *args):
192197
self.window.destroy()
193198
self.canvas.destroy()
194199

200+
@classmethod
201+
def start_main_loop(cls):
202+
global _application
203+
if _application is None:
204+
return
205+
206+
try:
207+
_application.run() # Quits when all added windows close.
208+
except KeyboardInterrupt:
209+
# Ensure all windows can process their close event from
210+
# _shutdown_application.
211+
context = GLib.MainContext.default()
212+
while context.pending():
213+
context.iteration(True)
214+
raise
215+
finally:
216+
# Running after quit is undefined, so create a new one next time.
217+
_application = None
218+
195219
def show(self):
196220
# show the figure window
197221
self.window.show()
@@ -305,22 +329,4 @@ class _BackendGTK(_Backend):
305329
Gtk.get_minor_version(),
306330
Gtk.get_micro_version(),
307331
)
308-
309-
@staticmethod
310-
def mainloop():
311-
global _application
312-
if _application is None:
313-
return
314-
315-
try:
316-
_application.run() # Quits when all added windows close.
317-
except KeyboardInterrupt:
318-
# Ensure all windows can process their close event from
319-
# _shutdown_application.
320-
context = GLib.MainContext.default()
321-
while context.pending():
322-
context.iteration(True)
323-
raise
324-
finally:
325-
# Running after quit is undefined, so create a new one next time.
326-
_application = None
332+
mainloop = _FigureManagerGTK.start_main_loop

‎lib/matplotlib/backends/_backend_tk.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/_backend_tk.py
+16-14Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,20 @@ def create_with_canvas(cls, canvas_class, figure, num):
484484
canvas.draw_idle()
485485
return manager
486486

487+
@classmethod
488+
def start_main_loop(cls):
489+
managers = Gcf.get_all_fig_managers()
490+
if managers:
491+
first_manager = managers[0]
492+
manager_class = type(first_manager)
493+
if manager_class._owns_mainloop:
494+
return
495+
manager_class._owns_mainloop = True
496+
try:
497+
first_manager.window.mainloop()
498+
finally:
499+
manager_class._owns_mainloop = False
500+
487501
def _update_window_dpi(self, *args):
488502
newdpi = self._window_dpi.get()
489503
self.window.call('tk', 'scaling', newdpi / 72)
@@ -1018,18 +1032,6 @@ def trigger(self, *args):
10181032
@_Backend.export
10191033
class _BackendTk(_Backend):
10201034
backend_version = tk.TkVersion
1035+
FigureCanvas = FigureCanvasTk
10211036
FigureManager = FigureManagerTk
1022-
1023-
@staticmethod
1024-
def mainloop():
1025-
managers = Gcf.get_all_fig_managers()
1026-
if managers:
1027-
first_manager = managers[0]
1028-
manager_class = type(first_manager)
1029-
if manager_class._owns_mainloop:
1030-
return
1031-
manager_class._owns_mainloop = True
1032-
try:
1033-
first_manager.window.mainloop()
1034-
finally:
1035-
manager_class._owns_mainloop = False
1037+
mainloop = FigureManagerTk.start_main_loop

‎lib/matplotlib/backends/backend_gtk3.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_gtk3.py
+5-6Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
import matplotlib as mpl
88
from matplotlib import _api, backend_tools, cbook
99
from matplotlib.backend_bases import (
10-
FigureCanvasBase, ToolContainerBase,
11-
CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent)
10+
ToolContainerBase, CloseEvent, KeyEvent, LocationEvent, MouseEvent,
11+
ResizeEvent)
1212

1313
try:
1414
import gi
@@ -26,8 +26,8 @@
2626

2727
from gi.repository import Gio, GLib, GObject, Gtk, Gdk
2828
from . import _backend_gtk
29-
from ._backend_gtk import (
30-
_BackendGTK, _FigureManagerGTK, _NavigationToolbar2GTK,
29+
from ._backend_gtk import ( # noqa: F401 # pylint: disable=W0611
30+
_BackendGTK, _FigureCanvasGTK, _FigureManagerGTK, _NavigationToolbar2GTK,
3131
TimerGTK as TimerGTK3,
3232
)
3333

@@ -52,9 +52,8 @@ def _mpl_to_gtk_cursor(mpl_cursor):
5252
_backend_gtk.mpl_to_gtk_cursor_name(mpl_cursor))
5353

5454

55-
class FigureCanvasGTK3(FigureCanvasBase, Gtk.DrawingArea):
55+
class FigureCanvasGTK3(_FigureCanvasGTK, Gtk.DrawingArea):
5656
required_interactive_framework = "gtk3"
57-
_timer_cls = TimerGTK3
5857
manager_class = _api.classproperty(lambda cls: FigureManagerGTK3)
5958
# Setting this as a static constant prevents
6059
# this resulting expression from leaking

‎lib/matplotlib/backends/backend_gtk4.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_gtk4.py
+4-6Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
import matplotlib as mpl
66
from matplotlib import _api, backend_tools, cbook
77
from matplotlib.backend_bases import (
8-
FigureCanvasBase, ToolContainerBase,
9-
KeyEvent, LocationEvent, MouseEvent, ResizeEvent)
8+
ToolContainerBase, KeyEvent, LocationEvent, MouseEvent, ResizeEvent)
109

1110
try:
1211
import gi
@@ -24,16 +23,15 @@
2423

2524
from gi.repository import Gio, GLib, Gtk, Gdk, GdkPixbuf
2625
from . import _backend_gtk
27-
from ._backend_gtk import (
28-
_BackendGTK, _FigureManagerGTK, _NavigationToolbar2GTK,
26+
from ._backend_gtk import ( # noqa: F401 # pylint: disable=W0611
27+
_BackendGTK, _FigureCanvasGTK, _FigureManagerGTK, _NavigationToolbar2GTK,
2928
TimerGTK as TimerGTK4,
3029
)
3130

3231

33-
class FigureCanvasGTK4(FigureCanvasBase, Gtk.DrawingArea):
32+
class FigureCanvasGTK4(_FigureCanvasGTK, Gtk.DrawingArea):
3433
required_interactive_framework = "gtk4"
3534
supports_blit = False
36-
_timer_cls = TimerGTK4
3735
manager_class = _api.classproperty(lambda cls: FigureManagerGTK4)
3836
_context_is_scaled = False
3937

‎lib/matplotlib/backends/backend_macosx.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_macosx.py
+5-4Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ def _close_button_pressed(self):
165165
def close(self):
166166
return self._close_button_pressed()
167167

168+
@classmethod
169+
def start_main_loop(cls):
170+
_macosx.show()
171+
168172
def show(self):
169173
if not self._shown:
170174
self._show()
@@ -177,7 +181,4 @@ def show(self):
177181
class _BackendMac(_Backend):
178182
FigureCanvas = FigureCanvasMac
179183
FigureManager = FigureManagerMac
180-
181-
@staticmethod
182-
def mainloop():
183-
_macosx.show()
184+
mainloop = FigureManagerMac.start_main_loop

‎lib/matplotlib/backends/backend_qt.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_qt.py
+8-6Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,13 @@ def resize(self, width, height):
583583
self.canvas.resize(width, height)
584584
self.window.resize(width + extra_width, height + extra_height)
585585

586+
@classmethod
587+
def start_main_loop(cls):
588+
qapp = QtWidgets.QApplication.instance()
589+
if qapp:
590+
with _maybe_allow_interrupt(qapp):
591+
qt_compat._exec(qapp)
592+
586593
def show(self):
587594
self.window.show()
588595
if mpl.rcParams['figure.raise_window']:
@@ -1007,9 +1014,4 @@ class _BackendQT(_Backend):
10071014
backend_version = __version__
10081015
FigureCanvas = FigureCanvasQT
10091016
FigureManager = FigureManagerQT
1010-
1011-
@staticmethod
1012-
def mainloop():
1013-
qapp = QtWidgets.QApplication.instance()
1014-
with _maybe_allow_interrupt(qapp):
1015-
qt_compat._exec(qapp)
1017+
mainloop = FigureManagerQT.start_main_loop

‎lib/matplotlib/backends/backend_webagg.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_webagg.py
+18-18Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ def run(self):
5353
class FigureManagerWebAgg(core.FigureManagerWebAgg):
5454
_toolbar2_class = core.NavigationToolbar2WebAgg
5555

56+
@classmethod
57+
def pyplot_show(cls, *, block=None):
58+
WebAggApplication.initialize()
59+
60+
url = "http://{address}:{port}{prefix}".format(
61+
address=WebAggApplication.address,
62+
port=WebAggApplication.port,
63+
prefix=WebAggApplication.url_prefix)
64+
65+
if mpl.rcParams['webagg.open_in_browser']:
66+
import webbrowser
67+
if not webbrowser.open(url):
68+
print("To view figure, visit {0}".format(url))
69+
else:
70+
print("To view figure, visit {0}".format(url))
71+
72+
WebAggApplication.start()
73+
5674

5775
class FigureCanvasWebAgg(core.FigureCanvasWebAggCore):
5876
manager_class = FigureManagerWebAgg
@@ -307,21 +325,3 @@ def ipython_inline_display(figure):
307325
class _BackendWebAgg(_Backend):
308326
FigureCanvas = FigureCanvasWebAgg
309327
FigureManager = FigureManagerWebAgg
310-
311-
@staticmethod
312-
def show(*, block=None):
313-
WebAggApplication.initialize()
314-
315-
url = "http://{address}:{port}{prefix}".format(
316-
address=WebAggApplication.address,
317-
port=WebAggApplication.port,
318-
prefix=WebAggApplication.url_prefix)
319-
320-
if mpl.rcParams['webagg.open_in_browser']:
321-
import webbrowser
322-
if not webbrowser.open(url):
323-
print("To view figure, visit {0}".format(url))
324-
else:
325-
print("To view figure, visit {0}".format(url))
326-
327-
WebAggApplication.start()

‎lib/matplotlib/backends/backend_wx.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_wx.py
+8-7Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,13 @@ def create_with_canvas(cls, canvas_class, figure, num):
999999
figure.canvas.draw_idle()
10001000
return manager
10011001

1002+
@classmethod
1003+
def start_main_loop(cls):
1004+
if not wx.App.IsMainLoopRunning():
1005+
wxapp = wx.GetApp()
1006+
if wxapp is not None:
1007+
wxapp.MainLoop()
1008+
10021009
def show(self):
10031010
# docstring inherited
10041011
self.frame.Show()
@@ -1365,10 +1372,4 @@ def trigger(self, *args, **kwargs):
13651372
class _BackendWx(_Backend):
13661373
FigureCanvas = FigureCanvasWx
13671374
FigureManager = FigureManagerWx
1368-
1369-
@staticmethod
1370-
def mainloop():
1371-
if not wx.App.IsMainLoopRunning():
1372-
wxapp = wx.GetApp()
1373-
if wxapp is not None:
1374-
wxapp.MainLoop()
1375+
mainloop = FigureManagerWx.start_main_loop

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.