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

Skip to content

Commit 10edb9a

Browse files
committed
Standardize creation of FigureManager from a given FigureCanvas class.
The `new_manager` classmethod may appear to belong more naturally to the FigureManager class rather than the FigureCanvas class, but putting it on FigureCanvas has multiple advantages: - One may want to create managers at times where all one has is a FigureCanvas instance, which may not even have a corresponding manager (e.g. `subplot_tool`). - A given FigureManager class can be associated with many different FigureCanvas classes (indeed, FigureManagerQT can manage both FigureCanvasQTAgg and FigureCanvasQTCairo and also the mplcairo Qt canvas classes), whereas we don't have multiple FigureManager classes for a given FigureCanvas class.
1 parent 2e248f5 commit 10edb9a

File tree

10 files changed

+118
-80
lines changed

10 files changed

+118
-80
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
``FigureFrameWx`` and subclasses
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
``FigureFrameWx.get_canvas`` was a helper for the ``FigureFrameWx`` constructor,
5+
and is now deprecated. ``FigureFrameWxAgg`` and ``FigureFrameWxCairo`` are
6+
deprecated in favor of passing the corresponding canvas class as the *canvas_cls*
7+
kwarg to ``FigureFrameWx``.

lib/matplotlib/backend_bases.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,13 @@
4646
from matplotlib import (
4747
_api, backend_tools as tools, cbook, colors, docstring, textpath,
4848
tight_bbox, transforms, widgets, get_backend, is_interactive, rcParams)
49+
from matplotlib._enums import JoinStyle, CapStyle
4950
from matplotlib._pylab_helpers import Gcf
5051
from matplotlib.backend_managers import ToolManager
5152
from matplotlib.cbook import _setattr_cm
5253
from matplotlib.path import Path
5354
from matplotlib.transforms import Affine2D
54-
from matplotlib._enums import JoinStyle, CapStyle
55+
from matplotlib.widgets import SubplotTool
5556

5657

5758
_log = logging.getLogger(__name__)
@@ -1762,6 +1763,16 @@ def _fix_ipython_backend2gui(cls):
17621763
if _is_non_interactive_terminal_ipython(ip):
17631764
ip.enable_gui(backend2gui_rif)
17641765

1766+
@classmethod
1767+
def new_manager(cls, figure, num):
1768+
"""
1769+
Create a new figure manager for *figure*, using this canvas class.
1770+
1771+
Backends should override this method to instantiate the correct figure
1772+
manager subclass, and perform any additional setup that may be needed.
1773+
"""
1774+
return FigureManagerBase(cls(figure), num)
1775+
17651776
@contextmanager
17661777
def _idle_draw_cntx(self):
17671778
self._is_idle_drawing = True
@@ -3308,9 +3319,16 @@ def _update_view(self):
33083319
self.canvas.draw_idle()
33093320

33103321
def configure_subplots(self, *args):
3311-
plt = _safe_pyplot_import()
3312-
self.subplot_tool = plt.subplot_tool(self.canvas.figure)
3313-
self.subplot_tool.figure.canvas.manager.show()
3322+
# This import needs to happen here due to circular imports.
3323+
from matplotlib.figure import Figure
3324+
with mpl.rc_context({"toolbar": "none"}): # No navbar for the toolfig.
3325+
manager = type(self.canvas).new_manager(Figure(figsize=(6, 3)), -1)
3326+
manager.set_window_title("Subplot configuration tool")
3327+
tool_fig = manager.canvas.figure
3328+
tool_fig.subplots_adjust(top=0.9)
3329+
manager.show()
3330+
self.subplot_tool = SubplotTool(self.canvas.figure, tool_fig)
3331+
return self.subplot_tool
33143332

33153333
def save_figure(self, *args):
33163334
"""Save the current figure."""
@@ -3549,9 +3567,7 @@ def new_figure_manager(cls, num, *args, **kwargs):
35493567
@classmethod
35503568
def new_figure_manager_given_figure(cls, num, figure):
35513569
"""Create a new figure manager instance for the given figure."""
3552-
canvas = cls.FigureCanvas(figure)
3553-
manager = cls.FigureManager(canvas, num)
3554-
return manager
3570+
return cls.FigureCanvas.new_manager(figure, num)
35553571

35563572
@classmethod
35573573
def draw_if_interactive(cls):

lib/matplotlib/backends/_backend_tk.py

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,35 @@ def filter_destroy(event):
209209
self._master = master
210210
self._tkcanvas.focus_set()
211211

212+
@classmethod
213+
def new_manager(cls, figure, num):
214+
# docstring inherited
215+
with _restore_foreground_window_at_end():
216+
if cbook._get_running_interactive_framework() is None:
217+
cbook._setup_new_guiapp()
218+
window = tk.Tk(className="matplotlib")
219+
window.withdraw()
220+
221+
# Put a Matplotlib icon on the window rather than the default tk
222+
# icon. Tkinter doesn't allow colour icons on linux systems, but
223+
# tk>=8.5 has a iconphoto command which we call directly. See
224+
# http://mail.python.org/pipermail/tkinter-discuss/2006-November/000954.html
225+
icon_fname = str(cbook._get_data_path(
226+
'images/matplotlib_128.ppm'))
227+
icon_img = tk.PhotoImage(file=icon_fname, master=window)
228+
try:
229+
window.iconphoto(False, icon_img)
230+
except Exception as exc:
231+
# log the failure (due e.g. to Tk version), but carry on
232+
_log.info('Could not load matplotlib icon: %s', exc)
233+
234+
canvas = cls(figure, master=window)
235+
manager = FigureManagerTk(canvas, num, window)
236+
if mpl.is_interactive():
237+
manager.show()
238+
canvas.draw_idle()
239+
return manager
240+
212241
def resize(self, event):
213242
width, height = event.width, event.height
214243
if self._resize_callback is not None:
@@ -859,37 +888,6 @@ def trigger(self, *args):
859888
class _BackendTk(_Backend):
860889
FigureManager = FigureManagerTk
861890

862-
@classmethod
863-
def new_figure_manager_given_figure(cls, num, figure):
864-
"""
865-
Create a new figure manager instance for the given figure.
866-
"""
867-
with _restore_foreground_window_at_end():
868-
if cbook._get_running_interactive_framework() is None:
869-
cbook._setup_new_guiapp()
870-
window = tk.Tk(className="matplotlib")
871-
window.withdraw()
872-
873-
# Put a Matplotlib icon on the window rather than the default tk
874-
# icon. Tkinter doesn't allow colour icons on linux systems, but
875-
# tk>=8.5 has a iconphoto command which we call directly. See
876-
# http://mail.python.org/pipermail/tkinter-discuss/2006-November/000954.html
877-
icon_fname = str(cbook._get_data_path(
878-
'images/matplotlib_128.ppm'))
879-
icon_img = tk.PhotoImage(file=icon_fname, master=window)
880-
try:
881-
window.iconphoto(False, icon_img)
882-
except Exception as exc:
883-
# log the failure (due e.g. to Tk version), but carry on
884-
_log.info('Could not load matplotlib icon: %s', exc)
885-
886-
canvas = cls.FigureCanvas(figure, master=window)
887-
manager = cls.FigureManager(canvas, num, window)
888-
if mpl.is_interactive():
889-
manager.show()
890-
canvas.draw_idle()
891-
return manager
892-
893891
@staticmethod
894892
def mainloop():
895893
managers = Gcf.get_all_fig_managers()

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ def __init__(self, figure=None):
142142
if renderer_init:
143143
renderer_init()
144144

145+
@classmethod
146+
def new_manager(cls, figure, num):
147+
# docstring inherited
148+
return FigureManagerGTK3(cls(figure), num)
149+
145150
@_api.deprecated("3.3", alternative="__init__")
146151
def _renderer_init(self):
147152
pass

lib/matplotlib/backends/backend_macosx.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ def __init__(self, figure):
3232
_macosx.FigureCanvas.__init__(self, width, height)
3333
self._dpi_ratio = 1.0
3434

35+
@classmethod
36+
def new_manager(cls, figure, num):
37+
# docstring inherited
38+
return FigureManagerMac(cls(figure), num)
39+
3540
def _set_device_scale(self, value):
3641
if self._dpi_ratio != value:
3742
# Need the new value in place before setting figure.dpi, which

lib/matplotlib/backends/backend_nbagg.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,20 @@ def remove_comm(self, comm_id):
142142

143143

144144
class FigureCanvasNbAgg(FigureCanvasWebAggCore):
145-
pass
145+
@classmethod
146+
def new_manager(cls, figure, num):
147+
canvas = cls(figure)
148+
manager = FigureManagerNbAgg(canvas, num)
149+
if is_interactive():
150+
manager.show()
151+
figure.canvas.draw_idle()
152+
153+
def destroy(event):
154+
canvas.mpl_disconnect(cid)
155+
Gcf.destroy(manager)
156+
157+
cid = canvas.mpl_connect('close_event', destroy)
158+
return manager
146159

147160

148161
class CommSocket:
@@ -227,21 +240,6 @@ class _BackendNbAgg(_Backend):
227240
FigureCanvas = FigureCanvasNbAgg
228241
FigureManager = FigureManagerNbAgg
229242

230-
@staticmethod
231-
def new_figure_manager_given_figure(num, figure):
232-
canvas = FigureCanvasNbAgg(figure)
233-
manager = FigureManagerNbAgg(canvas, num)
234-
if is_interactive():
235-
manager.show()
236-
figure.canvas.draw_idle()
237-
238-
def destroy(event):
239-
canvas.mpl_disconnect(cid)
240-
Gcf.destroy(manager)
241-
242-
cid = canvas.mpl_connect('close_event', destroy)
243-
return manager
244-
245243
@staticmethod
246244
def show(block=None):
247245
## TODO: something to do when keyword block==False ?

lib/matplotlib/backends/backend_qt5.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,11 @@ def __init__(self, figure=None):
233233
palette = QtGui.QPalette(QtCore.Qt.white)
234234
self.setPalette(palette)
235235

236+
@classmethod
237+
def new_manager(cls, figure, num):
238+
# docstring inherited
239+
return FigureManagerQT(cls(figure), num)
240+
236241
def _update_figure_dpi(self):
237242
dpi = self._dpi_ratio * self.figure._original_dpi
238243
self.figure._set_dpi(dpi, forward=False)

lib/matplotlib/backends/backend_wx.py

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Copyright (C) Jeremy O'Donoghue & John Hunter, 2003-4.
88
"""
99

10+
import functools
1011
import logging
1112
import math
1213
import pathlib
@@ -58,6 +59,15 @@ def DEBUG_MSG(string, lvl=3, o=None):
5859
IDLE_DELAY = 5 # Documented as deprecated as of Matplotlib 3.1.
5960

6061

62+
# lru_cache holds a reference to the App and prevents it from being gc'ed.
63+
@functools.lru_cache()
64+
def _create_wxapp():
65+
wxapp = wx.App(False)
66+
wxapp.SetExitOnFrameDelete(True)
67+
cbook._setup_new_guiapp()
68+
return wxapp
69+
70+
6171
def error_msg_wx(msg, parent=None):
6272
"""Signal an error condition with a popup error dialog."""
6373
dialog = wx.MessageDialog(parent=parent,
@@ -538,6 +548,17 @@ def __init__(self, parent, id, figure=None):
538548
self.SetBackgroundStyle(wx.BG_STYLE_PAINT) # Reduce flicker.
539549
self.SetBackgroundColour(wx.WHITE)
540550

551+
@classmethod
552+
def new_manager(cls, figure, num):
553+
# docstring inherited
554+
wxapp = wx.GetApp() or _create_wxapp()
555+
frame = FigureFrameWx(num, figure, canvas_cls=cls)
556+
figmgr = frame.get_figure_manager()
557+
if mpl.is_interactive():
558+
figmgr.frame.Show()
559+
figure.canvas.draw_idle()
560+
return figmgr
561+
541562
def Copy_to_Clipboard(self, event=None):
542563
"""Copy bitmap of canvas to system clipboard."""
543564
bmp_obj = wx.BitmapDataObject()
@@ -899,7 +920,7 @@ def _print_image(self, filename, filetype, *, quality=None):
899920

900921

901922
class FigureFrameWx(wx.Frame):
902-
def __init__(self, num, fig):
923+
def __init__(self, num, fig, *, canvas_cls=FigureCanvasWx):
903924
# On non-Windows platform, explicitly set the position - fix
904925
# positioning bug on some Linux platforms
905926
if wx.Platform == '__WXMSW__':
@@ -912,7 +933,7 @@ def __init__(self, num, fig):
912933
self.num = num
913934
_set_frame_icon(self)
914935

915-
self.canvas = self.get_canvas(fig)
936+
self.canvas = canvas_cls(self, -1, fig)
916937
w, h = map(math.ceil, fig.bbox.size)
917938
self.canvas.SetInitialSize(wx.Size(w, h))
918939
self.canvas.SetFocus()
@@ -960,6 +981,7 @@ def _get_toolbar(self):
960981
toolbar = None
961982
return toolbar
962983

984+
@_api.deprecated("3.5")
963985
def get_canvas(self, fig):
964986
return FigureCanvasWx(self, -1, fig)
965987

@@ -1484,29 +1506,6 @@ def trigger(self, *args, **kwargs):
14841506
class _BackendWx(_Backend):
14851507
FigureCanvas = FigureCanvasWx
14861508
FigureManager = FigureManagerWx
1487-
_frame_class = FigureFrameWx
1488-
1489-
@classmethod
1490-
def new_figure_manager(cls, num, *args, **kwargs):
1491-
# Create a wx.App instance if it has not been created so far.
1492-
wxapp = wx.GetApp()
1493-
if wxapp is None:
1494-
wxapp = wx.App(False)
1495-
wxapp.SetExitOnFrameDelete(True)
1496-
cbook._setup_new_guiapp()
1497-
# Retain a reference to the app object so that it does not get
1498-
# garbage collected.
1499-
_BackendWx._theWxApp = wxapp
1500-
return super().new_figure_manager(num, *args, **kwargs)
1501-
1502-
@classmethod
1503-
def new_figure_manager_given_figure(cls, num, figure):
1504-
frame = cls._frame_class(num, figure)
1505-
figmgr = frame.get_figure_manager()
1506-
if mpl.is_interactive():
1507-
figmgr.frame.Show()
1508-
figure.canvas.draw_idle()
1509-
return figmgr
15101509

15111510
@staticmethod
15121511
def mainloop():

lib/matplotlib/backends/backend_wxagg.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import wx
22

3+
from matplotlib import _api
34
from .backend_agg import FigureCanvasAgg
45
from .backend_wx import (
56
_BackendWx, _FigureCanvasWxBase, FigureFrameWx,
67
NavigationToolbar2Wx as NavigationToolbar2WxAgg)
78

89

10+
@_api.deprecated(
11+
"3.4", alternative="FigureFrameWx(..., canvas_cls=FigureCanvasWxAgg)")
912
class FigureFrameWxAgg(FigureFrameWx):
1013
def get_canvas(self, fig):
1114
return FigureCanvasWxAgg(self, -1, fig)

lib/matplotlib/backends/backend_wxcairo.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import wx.lib.wxcairo as wxcairo
22

3+
from matplotlib import _api
34
from .backend_cairo import cairo, FigureCanvasCairo, RendererCairo
45
from .backend_wx import (
56
_BackendWx, _FigureCanvasWxBase, FigureFrameWx,
67
NavigationToolbar2Wx as NavigationToolbar2WxCairo)
78

89

10+
@_api.deprecated(
11+
"3.5", alternative="FigureFrameWx(..., canvas_cls=FigureCanvasWxCairo)")
912
class FigureFrameWxCairo(FigureFrameWx):
1013
def get_canvas(self, fig):
1114
return FigureCanvasWxCairo(self, -1, fig)
@@ -44,4 +47,3 @@ def draw(self, drawDC=None):
4447
@_BackendWx.export
4548
class _BackendWxCairo(_BackendWx):
4649
FigureCanvas = FigureCanvasWxCairo
47-
_frame_class = FigureFrameWxCairo

0 commit comments

Comments
 (0)