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

Skip to content

Commit 89a5f9c

Browse files
committed
Deprecate NavigationToolbar2._init_toolbar.
As argued elsewhere, a customization point which requires third-party libraries to override a private method is awkward from the PoV of documentation and of required API stability. In fact _init_toolbar is not needed as a customization point; third-party libraries can simply override `__init__` and call `super().__init__` as appropriate. Moreover, *requiring* that `_init_toolbar` be overridden is actually overkill, e.g. for `test_backend_bases.py::test_interactive_zoom`: there, the base class NavigationToolbar2 is perfectly suitable -- see change there. In order to let third-parties write code that supports both pre- and post-deprecation versions of mpl, allow them to keep a fully empty `_init_toolbar` (an override is required by earlier versions of mpl) without triggering a deprecation warning.
1 parent da1ea05 commit 89a5f9c

File tree

10 files changed

+146
-140
lines changed

10 files changed

+146
-140
lines changed

doc/api/api_changes_3.3/deprecations.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,16 @@ The ``Fil``, ``Fill``, ``Filll``, ``NegFil``, ``NegFill``, ``NegFilll``, and
366366
``SsGlue`` classes in the :mod:`matplotlib.mathtext` module are deprecated.
367367
As an alternative, directly construct glue instances with ``Glue("fil")``, etc.
368368

369+
NavigationToolbar2._init_toolbar
370+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
371+
Overriding this method to initialize third-party toolbars is deprecated.
372+
Instead, the toolbar should be initialized in the ``__init__`` method of the
373+
subclass (which should call the base-class' ``__init__`` as appropriate). To
374+
keep back-compatibility with earlier versions of Matplotlib (which *required*
375+
``_init_toolbar`` to be overridden), a fully empty implementation (``def
376+
_init_toolbar(self): pass``) may be kept and will not trigger the deprecation
377+
warning.
378+
369379
NavigationToolbar2QT.parent and .basedir
370380
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
371381
These attributes are deprecated. In order to access the parent window, use

lib/matplotlib/backend_bases.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2668,11 +2668,11 @@ def __str__(self):
26682668

26692669
class NavigationToolbar2:
26702670
"""
2671-
Base class for the navigation cursor, version 2
2671+
Base class for the navigation cursor, version 2.
26722672
2673-
backends must implement a canvas that handles connections for
2673+
Backends must implement a canvas that handles connections for
26742674
'button_press_event' and 'button_release_event'. See
2675-
:meth:`FigureCanvasBase.mpl_connect` for more information
2675+
:meth:`FigureCanvasBase.mpl_connect` for more information.
26762676
26772677
They must also define
26782678
@@ -2682,9 +2682,6 @@ class NavigationToolbar2:
26822682
:meth:`set_cursor`
26832683
if you want the pointer icon to change
26842684
2685-
:meth:`_init_toolbar`
2686-
create your toolbar widget
2687-
26882685
:meth:`draw_rubberband` (optional)
26892686
draw the zoom to rect "rubberband" rectangle
26902687
@@ -2695,6 +2692,12 @@ class NavigationToolbar2:
26952692
you can change the history back / forward buttons to
26962693
indicate disabled / enabled state.
26972694
2695+
and override ``__init__`` to set up the toolbar -- without forgetting to
2696+
call the base-class init. Typically, ``__init__`` needs to set up toolbar
2697+
buttons connected to the `home`, `back`, `forward`, `pan`, `zoom`, and
2698+
`save_figure` methods and using standard icons in the "images" subdirectory
2699+
of the data path.
2700+
26982701
That's it, we'll do the rest!
26992702
"""
27002703

@@ -2724,7 +2727,15 @@ def __init__(self, canvas):
27242727
self._xypress = None # location and axis info at the time of the press
27252728
# This cursor will be set after the initial draw.
27262729
self._lastCursor = cursors.POINTER
2727-
self._init_toolbar()
2730+
2731+
init = cbook._deprecate_method_override(
2732+
__class__._init_toolbar, self, allow_empty=True, since="3.3",
2733+
addendum="Please fully initialize the toolbar in your subclass' "
2734+
"__init__; a fully empty _init_toolbar implementation may be kept "
2735+
"for compatibility with earlier versions of Matplotlib.")
2736+
if init:
2737+
init()
2738+
27282739
self._id_press = self.canvas.mpl_connect(
27292740
'button_press_event', self._zoom_pan_handler)
27302741
self._id_release = self.canvas.mpl_connect(
@@ -2787,6 +2798,7 @@ def home(self, *args):
27872798
self.set_history_buttons()
27882799
self._update_view()
27892800

2801+
@cbook.deprecated("3.3", alternative="__init__")
27902802
def _init_toolbar(self):
27912803
"""
27922804
This is where you actually build the GUI widgets (called by

lib/matplotlib/backends/_backend_tk.py

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,29 @@ def __init__(self, canvas, window, *, pack_toolbar=True):
491491
# Avoid using self.window (prefer self.canvas.get_tk_widget().master),
492492
# so that Tool implementations can reuse the methods.
493493
self.window = window
494+
495+
tk.Frame.__init__(self, master=window, borderwidth=2,
496+
width=int(canvas.figure.bbox.width), height=50)
497+
498+
self._buttons = {}
499+
for text, tooltip_text, image_file, callback in self.toolitems:
500+
if text is None:
501+
# Add a spacer; return value is unused.
502+
self._Spacer()
503+
else:
504+
self._buttons[text] = button = self._Button(
505+
text,
506+
str(cbook._get_data_path(f"images/{image_file}.gif")),
507+
toggle=callback in ["zoom", "pan"],
508+
command=getattr(self, callback),
509+
)
510+
if tooltip_text is not None:
511+
ToolTip.createToolTip(button, tooltip_text)
512+
513+
self.message = tk.StringVar(master=self)
514+
self._message_label = tk.Label(master=self, textvariable=self.message)
515+
self._message_label.pack(side=tk.RIGHT)
516+
494517
NavigationToolbar2.__init__(self, canvas)
495518
if pack_toolbar:
496519
self.pack(side=tk.BOTTOM, fill=tk.X)
@@ -547,51 +570,6 @@ def _Spacer(self):
547570
s.pack(side=tk.LEFT, padx=5)
548571
return s
549572

550-
def _init_toolbar(self):
551-
xmin, xmax = self.canvas.figure.bbox.intervalx
552-
height, width = 50, xmax-xmin
553-
tk.Frame.__init__(self, master=self.window,
554-
width=int(width), height=int(height),
555-
borderwidth=2)
556-
557-
self.update() # Make axes menu
558-
559-
self._buttons = {}
560-
for text, tooltip_text, image_file, callback in self.toolitems:
561-
if text is None:
562-
# Add a spacer; return value is unused.
563-
self._Spacer()
564-
else:
565-
self._buttons[text] = button = self._Button(
566-
text,
567-
str(cbook._get_data_path(f"images/{image_file}.gif")),
568-
toggle=callback in ["zoom", "pan"],
569-
command=getattr(self, callback),
570-
)
571-
if tooltip_text is not None:
572-
ToolTip.createToolTip(button, tooltip_text)
573-
574-
self.message = tk.StringVar(master=self)
575-
self._message_label = tk.Label(master=self, textvariable=self.message)
576-
self._message_label.pack(side=tk.RIGHT)
577-
578-
def _update_buttons_checked(self):
579-
for name, mode in [("Pan", "PAN"), ("Zoom", "ZOOM")]:
580-
button = self._buttons.get(name)
581-
if button:
582-
if self.mode.name == mode and not button.var.get():
583-
button.select()
584-
elif self.mode.name != mode and button.var.get():
585-
button.deselect()
586-
587-
def pan(self, *args):
588-
super().pan(*args)
589-
self._update_buttons_checked()
590-
591-
def zoom(self, *args):
592-
super().zoom(*args)
593-
self._update_buttons_checked()
594-
595573
def configure_subplots(self):
596574
toolfig = Figure(figsize=(6, 3))
597575
window = tk.Toplevel()

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -451,43 +451,7 @@ class NavigationToolbar2GTK3(NavigationToolbar2, Gtk.Toolbar):
451451
def __init__(self, canvas, window):
452452
self.win = window
453453
GObject.GObject.__init__(self)
454-
NavigationToolbar2.__init__(self, canvas)
455-
456-
@cbook.deprecated("3.3")
457-
@property
458-
def ctx(self):
459-
return self.canvas.get_property("window").cairo_create()
460-
461-
def set_message(self, s):
462-
self.message.set_label(s)
463-
464-
def set_cursor(self, cursor):
465-
self.canvas.get_property("window").set_cursor(cursord[cursor])
466-
Gtk.main_iteration()
467-
468-
def draw_rubberband(self, event, x0, y0, x1, y1):
469-
# adapted from
470-
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744
471-
ctx = self.canvas.get_property("window").cairo_create()
472-
473-
# todo: instead of redrawing the entire figure, copy the part of
474-
# the figure that was covered by the previous rubberband rectangle
475-
self.canvas.draw()
476454

477-
height = self.canvas.figure.bbox.height
478-
y1 = height - y1
479-
y0 = height - y0
480-
w = abs(x1 - x0)
481-
h = abs(y1 - y0)
482-
rect = [int(val) for val in (min(x0, x1), min(y0, y1), w, h)]
483-
484-
ctx.new_path()
485-
ctx.set_line_width(0.5)
486-
ctx.rectangle(*rect)
487-
ctx.set_source_rgb(0, 0, 0)
488-
ctx.stroke()
489-
490-
def _init_toolbar(self):
491455
self.set_style(Gtk.ToolbarStyle.ICONS)
492456

493457
self._gtk_ids = {}
@@ -521,6 +485,42 @@ def _init_toolbar(self):
521485

522486
self.show_all()
523487

488+
NavigationToolbar2.__init__(self, canvas)
489+
490+
@cbook.deprecated("3.3")
491+
@property
492+
def ctx(self):
493+
return self.canvas.get_property("window").cairo_create()
494+
495+
def set_message(self, s):
496+
self.message.set_label(s)
497+
498+
def set_cursor(self, cursor):
499+
self.canvas.get_property("window").set_cursor(cursord[cursor])
500+
Gtk.main_iteration()
501+
502+
def draw_rubberband(self, event, x0, y0, x1, y1):
503+
# adapted from
504+
# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/189744
505+
self.ctx = self.canvas.get_property("window").cairo_create()
506+
507+
# todo: instead of redrawing the entire figure, copy the part of
508+
# the figure that was covered by the previous rubberband rectangle
509+
self.canvas.draw()
510+
511+
height = self.canvas.figure.bbox.height
512+
y1 = height - y1
513+
y0 = height - y0
514+
w = abs(x1 - x0)
515+
h = abs(y1 - y0)
516+
rect = [int(val) for val in (min(x0, x1), min(y0, y1), w, h)]
517+
518+
self.ctx.new_path()
519+
self.ctx.set_line_width(0.5)
520+
self.ctx.rectangle(rect[0], rect[1], rect[2], rect[3])
521+
self.ctx.set_source_rgb(0, 0, 0)
522+
self.ctx.stroke()
523+
524524
def _update_buttons_checked(self):
525525
for name, active in [("Pan", "PAN"), ("Zoom", "ZOOM")]:
526526
button = self._gtk_ids.get(name)

lib/matplotlib/backends/backend_macosx.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,11 +110,10 @@ def close(self):
110110
class NavigationToolbar2Mac(_macosx.NavigationToolbar2, NavigationToolbar2):
111111

112112
def __init__(self, canvas):
113-
NavigationToolbar2.__init__(self, canvas)
114-
115-
def _init_toolbar(self):
113+
self.canvas = canvas # Needed by the _macosx __init__.
116114
_macosx.NavigationToolbar2.__init__(
117115
self, str(cbook._get_data_path('images')))
116+
NavigationToolbar2.__init__(self, canvas)
118117

119118
def draw_rubberband(self, event, x0, y0, x1, y1):
120119
self.canvas.set_rubberband(int(x0), int(y0), int(x1), int(y1))

lib/matplotlib/backends/backend_qt5.py

Lines changed: 27 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -639,36 +639,12 @@ class NavigationToolbar2QT(NavigationToolbar2, QtWidgets.QToolBar):
639639

640640
def __init__(self, canvas, parent, coordinates=True):
641641
"""coordinates: should we show the coordinates on the right?"""
642+
QtWidgets.QToolBar.__init__(self, parent)
643+
642644
self._parent = parent
643645
self.coordinates = coordinates
644646
self._actions = {} # mapping of toolitem method names to QActions.
645-
QtWidgets.QToolBar.__init__(self, parent)
646-
NavigationToolbar2.__init__(self, canvas)
647-
648-
@cbook.deprecated("3.3", alternative="self.canvas.parent()")
649-
@property
650-
def parent(self):
651-
return self._parent
652-
653-
@cbook.deprecated(
654-
"3.3", alternative="os.path.join(mpl.get_data_path(), 'images')")
655-
@property
656-
def basedir(self):
657-
return str(cbook._get_data_path('images'))
658-
659-
def _icon(self, name, color=None):
660-
if is_pyqt5():
661-
name = name.replace('.png', '_large.png')
662-
pm = QtGui.QPixmap(str(cbook._get_data_path('images', name)))
663-
qt_compat._setDevicePixelRatio(pm, self.canvas._dpi_ratio)
664-
if color is not None:
665-
mask = pm.createMaskFromColor(QtGui.QColor('black'),
666-
QtCore.Qt.MaskOutColor)
667-
pm.fill(color)
668-
pm.setMask(mask)
669-
return QtGui.QIcon(pm)
670647

671-
def _init_toolbar(self):
672648
background_color = self.palette().color(self.backgroundRole())
673649
foreground_color = self.palette().color(self.foregroundRole())
674650
icon_color = (foreground_color
@@ -699,6 +675,31 @@ def _init_toolbar(self):
699675
labelAction = self.addWidget(self.locLabel)
700676
labelAction.setVisible(True)
701677

678+
NavigationToolbar2.__init__(self, canvas)
679+
680+
@cbook.deprecated("3.3", alternative="self.canvas.parent()")
681+
@property
682+
def parent(self):
683+
return self._parent
684+
685+
@cbook.deprecated(
686+
"3.3", alternative="os.path.join(mpl.get_data_path(), 'images')")
687+
@property
688+
def basedir(self):
689+
return str(cbook._get_data_path('images'))
690+
691+
def _icon(self, name, color=None):
692+
if is_pyqt5():
693+
name = name.replace('.png', '_large.png')
694+
pm = QtGui.QPixmap(str(cbook._get_data_path('images', name)))
695+
qt_compat._setDevicePixelRatio(pm, qt_compat._devicePixelRatio(self))
696+
if color is not None:
697+
mask = pm.createMaskFromColor(QtGui.QColor('black'),
698+
QtCore.Qt.MaskOutColor)
699+
pm.fill(color)
700+
pm.setMask(mask)
701+
return QtGui.QIcon(pm)
702+
702703
def edit_parameters(self):
703704
axes = self.canvas.figure.get_axes()
704705
if not axes:

lib/matplotlib/backends/backend_webagg_core.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,9 +367,10 @@ class NavigationToolbar2WebAgg(backend_bases.NavigationToolbar2):
367367
if name_of_method in _ALLOWED_TOOL_ITEMS
368368
]
369369

370-
def _init_toolbar(self):
370+
def __init__(self, canvas):
371371
self.message = ''
372372
self.cursor = 0
373+
super().__init__(canvas)
373374

374375
def set_message(self, message):
375376
if message != self.message:

lib/matplotlib/backends/backend_wx.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,22 +1104,6 @@ def _set_frame_icon(frame):
11041104
class NavigationToolbar2Wx(NavigationToolbar2, wx.ToolBar):
11051105
def __init__(self, canvas):
11061106
wx.ToolBar.__init__(self, canvas.GetParent(), -1)
1107-
NavigationToolbar2.__init__(self, canvas)
1108-
self._idle = True
1109-
self.prevZoomRect = None
1110-
# for now, use alternate zoom-rectangle drawing on all
1111-
# Macs. N.B. In future versions of wx it may be possible to
1112-
# detect Retina displays with window.GetContentScaleFactor()
1113-
# and/or dc.GetContentScaleFactor()
1114-
self.retinaFix = 'wxMac' in wx.PlatformInfo
1115-
1116-
def get_canvas(self, frame, fig):
1117-
return type(self.canvas)(frame, -1, fig)
1118-
1119-
def _init_toolbar(self):
1120-
_log.debug("%s - _init_toolbar", type(self))
1121-
1122-
self._parent = self.canvas.GetParent()
11231107

11241108
self.wx_ids = {}
11251109
for text, tooltip_text, image_file, callback in self.toolitems:
@@ -1140,6 +1124,18 @@ def _init_toolbar(self):
11401124

11411125
self.Realize()
11421126

1127+
NavigationToolbar2.__init__(self, canvas)
1128+
self._idle = True
1129+
self.prevZoomRect = None
1130+
# for now, use alternate zoom-rectangle drawing on all
1131+
# Macs. N.B. In future versions of wx it may be possible to
1132+
# detect Retina displays with window.GetContentScaleFactor()
1133+
# and/or dc.GetContentScaleFactor()
1134+
self.retinaFix = 'wxMac' in wx.PlatformInfo
1135+
1136+
def get_canvas(self, frame, fig):
1137+
return type(self.canvas)(frame, -1, fig)
1138+
11431139
def zoom(self, *args):
11441140
self.ToggleTool(self.wx_ids['Pan'], False)
11451141
NavigationToolbar2.zoom(self, *args)

0 commit comments

Comments
 (0)