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

Skip to content

Commit 9fda6eb

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 5ba3911 commit 9fda6eb

File tree

9 files changed

+113
-78
lines changed

9 files changed

+113
-78
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: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
from matplotlib.path import Path
5252
from matplotlib.rcsetup import validate_joinstyle, validate_capstyle
5353
from matplotlib.transforms import Affine2D
54+
from matplotlib.widgets import SubplotTool
5455

5556

5657
_log = logging.getLogger(__name__)
@@ -1756,6 +1757,16 @@ def _fix_ipython_backend2gui(cls):
17561757
if _is_non_interactive_terminal_ipython(ip):
17571758
ip.enable_gui(backend2gui_rif)
17581759

1760+
@classmethod
1761+
def new_manager(cls, figure, num):
1762+
"""
1763+
Create a new figure manager for *figure*, using this canvas class.
1764+
1765+
Backends should override this method to instantiate the correct figure
1766+
manager subclass, and perform any additional setup that may be needed.
1767+
"""
1768+
return FigureManagerBase(cls(figure), num)
1769+
17591770
@contextmanager
17601771
def _idle_draw_cntx(self):
17611772
self._is_idle_drawing = True
@@ -3289,9 +3300,16 @@ def _update_view(self):
32893300
self.canvas.draw_idle()
32903301

32913302
def configure_subplots(self, *args):
3292-
plt = _safe_pyplot_import()
3293-
self.subplot_tool = plt.subplot_tool(self.canvas.figure)
3294-
self.subplot_tool.figure.canvas.manager.show()
3303+
# This import needs to happen here due to circular imports.
3304+
from matplotlib.figure import Figure
3305+
with mpl.rc_context({"toolbar": "none"}): # No navbar for the toolfig.
3306+
manager = type(self.canvas).new_manager(Figure(figsize=(6, 3)), -1)
3307+
manager.set_window_title("Subplot configuration tool")
3308+
tool_fig = manager.canvas.figure
3309+
tool_fig.subplots_adjust(top=0.9)
3310+
manager.show()
3311+
self.subplot_tool = SubplotTool(self.canvas.figure, tool_fig)
3312+
return self.subplot_tool
32953313

32963314
def save_figure(self, *args):
32973315
"""Save the current figure."""
@@ -3533,9 +3551,7 @@ def new_figure_manager(cls, num, *args, **kwargs):
35333551
@classmethod
35343552
def new_figure_manager_given_figure(cls, num, figure):
35353553
"""Create a new figure manager instance for the given figure."""
3536-
canvas = cls.FigureCanvas(figure)
3537-
manager = cls.FigureManager(canvas, num)
3538-
return manager
3554+
return cls.FigureCanvas.new_manager(figure, num)
35393555

35403556
@classmethod
35413557
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
@@ -160,6 +160,35 @@ def filter_destroy(event):
160160
self._master = master
161161
self._tkcanvas.focus_set()
162162

163+
@classmethod
164+
def new_manager(cls, figure, num):
165+
# docstring inherited
166+
with _restore_foreground_window_at_end():
167+
if cbook._get_running_interactive_framework() is None:
168+
cbook._setup_new_guiapp()
169+
window = tk.Tk(className="matplotlib")
170+
window.withdraw()
171+
172+
# Put a Matplotlib icon on the window rather than the default tk
173+
# icon. Tkinter doesn't allow colour icons on linux systems, but
174+
# tk>=8.5 has a iconphoto command which we call directly. See
175+
# http://mail.python.org/pipermail/tkinter-discuss/2006-November/000954.html
176+
icon_fname = str(cbook._get_data_path(
177+
'images/matplotlib_128.ppm'))
178+
icon_img = tk.PhotoImage(file=icon_fname, master=window)
179+
try:
180+
window.iconphoto(False, icon_img)
181+
except Exception as exc:
182+
# log the failure (due e.g. to Tk version), but carry on
183+
_log.info('Could not load matplotlib icon: %s', exc)
184+
185+
canvas = cls(figure, master=window)
186+
manager = FigureManagerTk(canvas, num, window)
187+
if mpl.is_interactive():
188+
manager.show()
189+
canvas.draw_idle()
190+
return manager
191+
163192
def resize(self, event):
164193
width, height = event.width, event.height
165194
if self._resize_callback is not None:
@@ -796,37 +825,6 @@ def trigger(self, *args):
796825
class _BackendTk(_Backend):
797826
FigureManager = FigureManagerTk
798827

799-
@classmethod
800-
def new_figure_manager_given_figure(cls, num, figure):
801-
"""
802-
Create a new figure manager instance for the given figure.
803-
"""
804-
with _restore_foreground_window_at_end():
805-
if cbook._get_running_interactive_framework() is None:
806-
cbook._setup_new_guiapp()
807-
window = tk.Tk(className="matplotlib")
808-
window.withdraw()
809-
810-
# Put a Matplotlib icon on the window rather than the default tk
811-
# icon. Tkinter doesn't allow colour icons on linux systems, but
812-
# tk>=8.5 has a iconphoto command which we call directly. See
813-
# http://mail.python.org/pipermail/tkinter-discuss/2006-November/000954.html
814-
icon_fname = str(cbook._get_data_path(
815-
'images/matplotlib_128.ppm'))
816-
icon_img = tk.PhotoImage(file=icon_fname, master=window)
817-
try:
818-
window.iconphoto(False, icon_img)
819-
except Exception as exc:
820-
# log the failure (due e.g. to Tk version), but carry on
821-
_log.info('Could not load matplotlib icon: %s', exc)
822-
823-
canvas = cls.FigureCanvas(figure, master=window)
824-
manager = cls.FigureManager(canvas, num, window)
825-
if mpl.is_interactive():
826-
manager.show()
827-
canvas.draw_idle()
828-
return manager
829-
830828
@staticmethod
831829
def trigger_manager_draw(manager):
832830
manager.show()

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ def __init__(self, figure):
136136
if renderer_init:
137137
renderer_init()
138138

139+
@classmethod
140+
def new_manager(cls, figure, num):
141+
# docstring inherited
142+
return FigureManagerGTK3(cls(figure), num)
143+
139144
@cbook.deprecated("3.3", alternative="__init__")
140145
def _renderer_init(self):
141146
pass

lib/matplotlib/backends/backend_nbagg.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,21 @@ def remove_comm(self, comm_id):
144144
class FigureCanvasNbAgg(FigureCanvasWebAggCore):
145145
_timer_cls = TimerTornado
146146

147+
@classmethod
148+
def new_manager(cls, figure, num):
149+
canvas = cls(figure)
150+
manager = FigureManagerNbAgg(canvas, num)
151+
if is_interactive():
152+
manager.show()
153+
figure.canvas.draw_idle()
154+
155+
def destroy(event):
156+
canvas.mpl_disconnect(cid)
157+
Gcf.destroy(manager)
158+
159+
cid = canvas.mpl_connect('close_event', destroy)
160+
return manager
161+
147162

148163
class CommSocket:
149164
"""
@@ -227,21 +242,6 @@ class _BackendNbAgg(_Backend):
227242
FigureCanvas = FigureCanvasNbAgg
228243
FigureManager = FigureManagerNbAgg
229244

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-
245245
@staticmethod
246246
def trigger_manager_draw(manager):
247247
manager.show()

lib/matplotlib/backends/backend_qt5.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ def __init__(self, figure):
240240
palette = QtGui.QPalette(QtCore.Qt.white)
241241
self.setPalette(palette)
242242

243+
@classmethod
244+
def new_manager(cls, figure, num):
245+
# docstring inherited
246+
return FigureManagerQT(cls(figure), num)
247+
243248
def _update_figure_dpi(self):
244249
dpi = self._dpi_ratio * self.figure._original_dpi
245250
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,
@@ -536,6 +546,17 @@ def __init__(self, parent, id, figure):
536546
self.SetBackgroundStyle(wx.BG_STYLE_PAINT) # Reduce flicker.
537547
self.SetBackgroundColour(wx.WHITE)
538548

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

896917

897918
class FigureFrameWx(wx.Frame):
898-
def __init__(self, num, fig):
919+
def __init__(self, num, fig, *, canvas_cls=FigureCanvasWx):
899920
# On non-Windows platform, explicitly set the position - fix
900921
# positioning bug on some Linux platforms
901922
if wx.Platform == '__WXMSW__':
@@ -908,7 +929,7 @@ def __init__(self, num, fig):
908929
self.num = num
909930
_set_frame_icon(self)
910931

911-
self.canvas = self.get_canvas(fig)
932+
self.canvas = canvas_cls(self, -1, fig)
912933
self.canvas.SetInitialSize(wx.Size(fig.bbox.width, fig.bbox.height))
913934
self.canvas.SetFocus()
914935
self.sizer = wx.BoxSizer(wx.VERTICAL)
@@ -955,6 +976,7 @@ def _get_toolbar(self):
955976
toolbar = None
956977
return toolbar
957978

979+
@cbook.deprecated("3.4")
958980
def get_canvas(self, fig):
959981
return FigureCanvasWx(self, -1, fig)
960982

@@ -1477,34 +1499,11 @@ def trigger(self, *args, **kwargs):
14771499
class _BackendWx(_Backend):
14781500
FigureCanvas = FigureCanvasWx
14791501
FigureManager = FigureManagerWx
1480-
_frame_class = FigureFrameWx
14811502

14821503
@staticmethod
14831504
def trigger_manager_draw(manager):
14841505
manager.canvas.draw_idle()
14851506

1486-
@classmethod
1487-
def new_figure_manager(cls, num, *args, **kwargs):
1488-
# Create a wx.App instance if it has not been created so far.
1489-
wxapp = wx.GetApp()
1490-
if wxapp is None:
1491-
wxapp = wx.App(False)
1492-
wxapp.SetExitOnFrameDelete(True)
1493-
cbook._setup_new_guiapp()
1494-
# Retain a reference to the app object so that it does not get
1495-
# garbage collected.
1496-
_BackendWx._theWxApp = wxapp
1497-
return super().new_figure_manager(num, *args, **kwargs)
1498-
1499-
@classmethod
1500-
def new_figure_manager_given_figure(cls, num, figure):
1501-
frame = cls._frame_class(num, figure)
1502-
figmgr = frame.get_figure_manager()
1503-
if mpl.is_interactive():
1504-
figmgr.frame.Show()
1505-
figure.canvas.draw_idle()
1506-
return figmgr
1507-
15081507
@staticmethod
15091508
def mainloop():
15101509
if not wx.App.IsMainLoopRunning():

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 cbook
34
from .backend_agg import FigureCanvasAgg
45
from .backend_wx import (
56
_BackendWx, _FigureCanvasWxBase, FigureFrameWx,
67
NavigationToolbar2Wx as NavigationToolbar2WxAgg)
78

89

10+
@cbook.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 cbook
34
from .backend_cairo import cairo, FigureCanvasCairo, RendererCairo
45
from .backend_wx import (
56
_BackendWx, _FigureCanvasWxBase, FigureFrameWx,
67
NavigationToolbar2Wx as NavigationToolbar2WxCairo)
78

89

10+
@cbook.deprecated(
11+
"3.4", 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)