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

Skip to content

Commit e32ae61

Browse files
committed
Move show() to somewhere naturally inheritable.
It's actually not clear whether to move it to FigureCanvas or FigureManager. FigureCanvas already has start_event_loop (cf. start_main_loop), but pyplot_show/start_main_loop is more a global/pyplot-only concept, so perhaps it belongs to FigureManager instead? OTOH, being on canvas_class makes it easier for switch_backend to access it (it doesn't need to go through `canvas_class.manager_class.pyplot_show`, which is relevant considering that some designs of inheritable backends didn't even have a manager_class attribute).
1 parent 231d1c8 commit e32ae61

11 files changed

+168
-90
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2840,6 +2840,48 @@ def create_with_canvas(cls, canvas_class, figure, num):
28402840
"""
28412841
return cls(canvas_class(figure), num)
28422842

2843+
@classmethod
2844+
def start_main_loop(cls):
2845+
"""
2846+
Start the main event loop.
2847+
2848+
Interactive backends need to reimplement this method or
2849+
`~.FigureManagerBase.pyplot_show`.
2850+
"""
2851+
2852+
@classmethod
2853+
def pyplot_show(cls, *, block=None):
2854+
"""
2855+
Show all figures. This method is the implementation of `.pyplot.show`.
2856+
2857+
Interactive backends need to reimplement this method or
2858+
`~.FigureManagerBase.start_main_loop`.
2859+
2860+
Parameters
2861+
----------
2862+
block : bool, optional
2863+
Whether to block by calling ``start_main_loop``. The default,
2864+
None, means to block if we are neither in IPython's ``%pylab`` mode
2865+
nor in ``interactive`` mode.
2866+
"""
2867+
managers = Gcf.get_all_fig_managers()
2868+
if not managers:
2869+
return
2870+
for manager in managers:
2871+
try:
2872+
manager.show() # Emits a warning for non-interactive backend.
2873+
except NonGuiException as exc:
2874+
_api.warn_external(str(exc))
2875+
if block is None:
2876+
# Hack: Are we in IPython's %pylab mode? In pylab mode, IPython
2877+
# (>= 0.10) tacks a _needmain attribute onto pyplot.show (always
2878+
# set to False).
2879+
ipython_pylab = hasattr(
2880+
getattr(sys.modules.get("pyplot"), "show", None), "_needmain")
2881+
block = not ipython_pylab and not is_interactive()
2882+
if block:
2883+
cls.start_main_loop()
2884+
28432885
def show(self):
28442886
"""
28452887
For GUI backends, show the figure window and redraw.
@@ -3518,7 +3560,11 @@ def new_figure_manager_given_figure(cls, num, figure):
35183560

35193561
@classmethod
35203562
def draw_if_interactive(cls):
3521-
if cls.mainloop is not None and is_interactive():
3563+
manager_class = cls.FigureCanvas.manager_class
3564+
backend_is_interactive = (
3565+
manager_class.start_main_loop != FigureManagerBase.start_main_loop
3566+
or manager_class.pyplot_show != FigureManagerBase.pyplot_show)
3567+
if backend_is_interactive and is_interactive():
35223568
manager = Gcf.get_active()
35233569
if manager:
35243570
manager.canvas.draw_idle()
@@ -3546,8 +3592,8 @@ def show(cls, *, block=None):
35463592
# Hack: Are we in IPython's %pylab mode? In pylab mode, IPython
35473593
# (>= 0.10) tacks a _needmain attribute onto pyplot.show (always
35483594
# set to False).
3549-
from matplotlib import pyplot
3550-
ipython_pylab = hasattr(pyplot.show, "_needmain")
3595+
ipython_pylab = hasattr(
3596+
getattr(sys.modules.get("pyplot"), "show", None), "_needmain")
35513597
block = not ipython_pylab and not is_interactive()
35523598
if block:
35533599
cls.mainloop()

lib/matplotlib/backends/_backend_gtk.py

Lines 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

Lines 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

Lines 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

Lines 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

Lines 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

Lines 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

Lines 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

Lines 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)