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

Skip to content

Commit 5f1d623

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 36685eb commit 5f1d623

11 files changed

+163
-87
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2822,6 +2822,46 @@ def create_with_canvas(cls, canvas_class, figure, num):
28222822
"""
28232823
return cls(canvas_class(figure), num)
28242824

2825+
@classmethod
2826+
def start_main_loop(cls):
2827+
"""
2828+
Start the main event loop.
2829+
2830+
Interactive backends need to reimplement this method or
2831+
`~.FigureManagerBase.pyplot_show`.
2832+
"""
2833+
2834+
@classmethod
2835+
def pyplot_show(cls, *, block=None):
2836+
"""
2837+
Show all figures. This method is the implementation of `.pyplot.show`.
2838+
2839+
Interactive backends need to reimplement this method or
2840+
`~.FigureManagerBase.start_main_loop`.
2841+
2842+
Parameters
2843+
----------
2844+
block : bool, optional
2845+
Whether to block by calling ``start_main_loop``. The default,
2846+
None, means to block if we are neither in IPython's ``%pylab`` mode
2847+
nor in ``interactive`` mode.
2848+
"""
2849+
managers = Gcf.get_all_fig_managers()
2850+
if not managers:
2851+
return
2852+
for manager in managers:
2853+
try:
2854+
manager.show() # Emits a warning for non-interactive backend.
2855+
except NonGuiException as exc:
2856+
_api.warn_external(str(exc))
2857+
if block is None:
2858+
# Hack: Are we in IPython's pylab mode?
2859+
from matplotlib import pyplot
2860+
ipython_pylab = hasattr(pyplot.show, "_needmain")
2861+
block = not ipython_pylab and not is_interactive()
2862+
if block:
2863+
cls.start_main_loop()
2864+
28252865
def show(self):
28262866
"""
28272867
For GUI backends, show the figure window and redraw.
@@ -3500,7 +3540,11 @@ def new_figure_manager_given_figure(cls, num, figure):
35003540

35013541
@classmethod
35023542
def draw_if_interactive(cls):
3503-
if cls.mainloop is not None and is_interactive():
3543+
manager_class = cls.FigureCanvas.manager_class
3544+
backend_is_interactive = (
3545+
manager_class.start_main_loop != FigureManagerBase.start_main_loop
3546+
or manager_class.pyplot_show != FigureManagerBase.pyplot_show)
3547+
if backend_is_interactive and is_interactive():
35043548
manager = Gcf.get_active()
35053549
if manager:
35063550
manager.canvas.draw_idle()

lib/matplotlib/backends/_backend_gtk.py

Lines changed: 26 additions & 19 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
@@ -118,6 +119,10 @@ def _on_timer(self):
118119
return False
119120

120121

122+
class _FigureCanvasGTK(FigureCanvasBase):
123+
_timer_cls = TimerGTK
124+
125+
121126
class _FigureManagerGTK(FigureManagerBase):
122127
"""
123128
Attributes
@@ -197,6 +202,25 @@ def destroy(self, *args):
197202
self.window.destroy()
198203
self.canvas.destroy()
199204

205+
@classmethod
206+
def start_main_loop(cls):
207+
global _application
208+
if _application is None:
209+
return
210+
211+
try:
212+
_application.run() # Quits when all added windows close.
213+
except KeyboardInterrupt:
214+
# Ensure all windows can process their close event from
215+
# _shutdown_application.
216+
context = GLib.MainContext.default()
217+
while context.pending():
218+
context.iteration(True)
219+
raise
220+
finally:
221+
# Running after quit is undefined, so create a new one next time.
222+
_application = None
223+
200224
def show(self):
201225
# show the figure window
202226
self.window.show()
@@ -305,21 +329,4 @@ def trigger(self, *args):
305329

306330

307331
class _BackendGTK(_Backend):
308-
@staticmethod
309-
def mainloop():
310-
global _application
311-
if _application is None:
312-
return
313-
314-
try:
315-
_application.run() # Quits when all added windows close.
316-
except KeyboardInterrupt:
317-
# Ensure all windows can process their close event from
318-
# _shutdown_application.
319-
context = GLib.MainContext.default()
320-
while context.pending():
321-
context.iteration(True)
322-
raise
323-
finally:
324-
# Running after quit is undefined, so create a new one next time.
325-
_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
@@ -486,6 +486,20 @@ def create_with_canvas(cls, canvas_class, figure, num):
486486
canvas.draw_idle()
487487
return manager
488488

489+
@classmethod
490+
def start_main_loop(cls):
491+
managers = Gcf.get_all_fig_managers()
492+
if managers:
493+
first_manager = managers[0]
494+
manager_class = type(first_manager)
495+
if manager_class._owns_mainloop:
496+
return
497+
manager_class._owns_mainloop = True
498+
try:
499+
first_manager.window.mainloop()
500+
finally:
501+
manager_class._owns_mainloop = False
502+
489503
def _update_window_dpi(self, *args):
490504
newdpi = self._window_dpi.get()
491505
self.window.call('tk', 'scaling', newdpi / 72)
@@ -1010,18 +1024,6 @@ def trigger(self, *args):
10101024

10111025
@_Backend.export
10121026
class _BackendTk(_Backend):
1027+
FigureCanvas = FigureCanvasTk
10131028
FigureManager = FigureManagerTk
1014-
1015-
@staticmethod
1016-
def mainloop():
1017-
managers = Gcf.get_all_fig_managers()
1018-
if managers:
1019-
first_manager = managers[0]
1020-
manager_class = type(first_manager)
1021-
if manager_class._owns_mainloop:
1022-
return
1023-
manager_class._owns_mainloop = True
1024-
try:
1025-
first_manager.window.mainloop()
1026-
finally:
1027-
manager_class._owns_mainloop = False
1029+
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
from ._backend_gtk import backend_version # noqa: F401 # pylint: disable=W0611
@@ -53,9 +53,8 @@ def _mpl_to_gtk_cursor(mpl_cursor):
5353
_backend_gtk.mpl_to_gtk_cursor_name(mpl_cursor))
5454

5555

56-
class FigureCanvasGTK3(FigureCanvasBase, Gtk.DrawingArea):
56+
class FigureCanvasGTK3(_FigureCanvasGTK, Gtk.DrawingArea):
5757
required_interactive_framework = "gtk3"
58-
_timer_cls = TimerGTK3
5958
manager_class = _api.classproperty(lambda cls: FigureManagerGTK3)
6059
# Setting this as a static constant prevents
6160
# 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,17 +23,16 @@
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
from ._backend_gtk import backend_version # noqa: F401 # pylint: disable=W0611
3231

3332

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

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: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,12 @@ def resize(self, width, height):
585585
self.canvas.resize(width, height)
586586
self.window.resize(width + extra_width, height + extra_height)
587587

588+
@classmethod
589+
def start_main_loop(cls):
590+
qapp = QtWidgets.QApplication.instance()
591+
with _maybe_allow_interrupt(qapp):
592+
qt_compat._exec(qapp)
593+
588594
def show(self):
589595
self.window.show()
590596
if mpl.rcParams['figure.raise_window']:
@@ -1008,9 +1014,4 @@ def trigger(self, *args, **kwargs):
10081014
class _BackendQT(_Backend):
10091015
FigureCanvas = FigureCanvasQT
10101016
FigureManager = FigureManagerQT
1011-
1012-
@staticmethod
1013-
def mainloop():
1014-
qapp = QtWidgets.QApplication.instance()
1015-
with _maybe_allow_interrupt(qapp):
1016-
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
@@ -997,6 +997,13 @@ def create_with_canvas(cls, canvas_class, figure, num):
997997
figure.canvas.draw_idle()
998998
return manager
999999

1000+
@classmethod
1001+
def start_main_loop(cls):
1002+
if not wx.App.IsMainLoopRunning():
1003+
wxapp = wx.GetApp()
1004+
if wxapp is not None:
1005+
wxapp.MainLoop()
1006+
10001007
def show(self):
10011008
# docstring inherited
10021009
self.frame.Show()
@@ -1363,10 +1370,4 @@ def trigger(self, *args, **kwargs):
13631370
class _BackendWx(_Backend):
13641371
FigureCanvas = FigureCanvasWx
13651372
FigureManager = FigureManagerWx
1366-
1367-
@staticmethod
1368-
def mainloop():
1369-
if not wx.App.IsMainLoopRunning():
1370-
wxapp = wx.GetApp()
1371-
if wxapp is not None:
1372-
wxapp.MainLoop()
1373+
mainloop = FigureManagerWx.start_main_loop

0 commit comments

Comments
 (0)