Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 89a5f9c

Browse filesBrowse 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
Copy full SHA for 89a5f9c

File tree

Expand file treeCollapse file tree

10 files changed

+146
-140
lines changed
Filter options
Expand file treeCollapse file tree

10 files changed

+146
-140
lines changed

‎doc/api/api_changes_3.3/deprecations.rst

Copy file name to clipboardExpand all lines: doc/api/api_changes_3.3/deprecations.rst
+10Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backend_bases.py
+19-7Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/_backend_tk.py
+23-45Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_gtk3.py
+36-36Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_macosx.py
+2-3Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_qt5.py
+27-26Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_webagg_core.py
+2-1Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_wx.py
+12-16Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.