Skip to content

Commit 3dbe623

Browse files
committed
Explicit tool registration.
1 parent 31e851a commit 3dbe623

File tree

6 files changed

+113
-68
lines changed

6 files changed

+113
-68
lines changed

lib/matplotlib/backend_managers.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ def remove_tool(self, name):
234234

235235
del self._tools[name]
236236

237-
def add_tool(self, name, tool, *args, **kwargs):
237+
def add_tool(self, tool_name):
238238
"""
239239
Add *tool* to `ToolManager`
240240
@@ -258,20 +258,18 @@ def add_tool(self, name, tool, *args, **kwargs):
258258
matplotlib.backend_tools.ToolBase : The base class for tools.
259259
"""
260260

261-
tool_cls = self._get_cls_to_instantiate(tool)
262-
if not tool_cls:
263-
raise ValueError('Impossible to find class for %s' % str(tool))
261+
tool_cls = tools.get_tool(tool_name, self.canvas.__module__)
264262

265-
if name in self._tools:
263+
if tool_name in self._tools:
266264
warnings.warn('A "Tool class" with the same name already exists, '
267265
'not added')
268-
return self._tools[name]
266+
return self._tools[tool_name]
269267

270-
tool_obj = tool_cls(self, name, *args, **kwargs)
271-
self._tools[name] = tool_obj
268+
tool_obj = tool_cls(self, tool_name)
269+
self._tools[tool_name] = tool_obj
272270

273271
if tool_cls.default_keymap is not None:
274-
self.update_keymap(name, tool_cls.default_keymap)
272+
self.update_keymap(tool_name, tool_cls.default_keymap)
275273

276274
# For toggle tools init the radio_group in self._toggled
277275
if isinstance(tool_obj, tools.ToolToggleBase):

lib/matplotlib/backend_tools.py

Lines changed: 90 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,60 @@
1111
`matplotlib.backend_managers.ToolManager`
1212
"""
1313

14-
15-
from matplotlib import rcParams
16-
from matplotlib._pylab_helpers import Gcf
17-
import matplotlib.cbook as cbook
18-
from weakref import WeakKeyDictionary
1914
import six
15+
16+
import functools
2017
import time
2118
import warnings
19+
from weakref import WeakKeyDictionary
20+
2221
import numpy as np
2322

23+
from matplotlib import rcParams
24+
from matplotlib._pylab_helpers import Gcf
25+
import matplotlib.cbook as cbook
26+
2427

2528
class Cursors(object):
2629
"""Simple namespace for cursor reference"""
2730
HAND, POINTER, SELECT_REGION, MOVE, WAIT = list(range(5))
2831
cursors = Cursors()
2932

30-
# Views positions tool
31-
_views_positions = 'viewpos'
33+
34+
_registered_tools = {}
35+
_tools_inheritance = {}
36+
37+
38+
def register_tool(name, backend="", cls=None):
39+
"""Declares that class *cls* implements tool *name* for backend *backend*.
40+
41+
Can be used as a class decorator.
42+
"""
43+
if cls is None:
44+
return functools.partial(register_tool, name, backend)
45+
if (name, backend) in _registered_tools:
46+
raise KeyError("Tool {!r} is already registered for backend {!r}"
47+
.format(name, backend))
48+
_registered_tools[name, backend] = cls
49+
return cls
50+
51+
52+
def _inherit_tools(child, parent):
53+
"""Declares that backend *child* should default to the tools of *parent*.
54+
"""
55+
_tools_inheritance[child] = parent
56+
57+
58+
def get_tool(name, backend):
59+
"""Get the tool class for tool name *name* and backend *backend*.
60+
"""
61+
try:
62+
return _registered_tools[name, backend]
63+
except KeyError:
64+
try:
65+
return _registered_tools[name, _tools_inheritance[backend]]
66+
except KeyError:
67+
return _registered_tools[name, ""]
3268

3369

3470
class ToolBase(object):
@@ -305,6 +341,7 @@ def set_cursor(self, cursor):
305341
raise NotImplementedError
306342

307343

344+
@register_tool("position")
308345
class ToolCursorPosition(ToolBase):
309346
"""
310347
Send message with the current pointer position
@@ -378,6 +415,7 @@ def remove_rubberband(self):
378415
pass
379416

380417

418+
@register_tool("quit")
381419
class ToolQuit(ToolBase):
382420
"""Tool to call the figure manager destroy method"""
383421

@@ -388,6 +426,7 @@ def trigger(self, sender, event, data=None):
388426
Gcf.destroy_fig(self.figure)
389427

390428

429+
@register_tool("quit_all")
391430
class ToolQuitAll(ToolBase):
392431
"""Tool to call the figure manager destroy method"""
393432

@@ -398,6 +437,7 @@ def trigger(self, sender, event, data=None):
398437
Gcf.destroy_all()
399438

400439

440+
@register_tool("allnav")
401441
class ToolEnableAllNavigation(ToolBase):
402442
"""Tool to enable all axes for toolmanager interaction"""
403443

@@ -414,6 +454,7 @@ def trigger(self, sender, event, data=None):
414454
a.set_navigate(True)
415455

416456

457+
@register_tool("nav")
417458
class ToolEnableNavigation(ToolBase):
418459
"""Tool to enable a specific axes for toolmanager interaction"""
419460

@@ -465,6 +506,7 @@ def _get_uniform_grid_state(ticks):
465506
return None
466507

467508

509+
@register_tool("grid")
468510
class ToolGrid(_ToolGridBase):
469511
"""Tool to toggle the major grids of the figure"""
470512

@@ -486,6 +528,7 @@ def _get_next_grid_states(self, ax):
486528
y_state, "major" if y_state else "both")
487529

488530

531+
@register_tool("grid_minor")
489532
class ToolMinorGrid(_ToolGridBase):
490533
"""Tool to toggle the major and minor grids of the figure"""
491534

@@ -506,6 +549,7 @@ def _get_next_grid_states(self, ax):
506549
return x_state, "both", y_state, "both"
507550

508551

552+
@register_tool("fullscreen")
509553
class ToolFullScreen(ToolToggleBase):
510554
"""Tool to toggle full screen"""
511555

@@ -536,16 +580,7 @@ def disable(self, event):
536580
self.figure.canvas.draw_idle()
537581

538582

539-
class ToolYScale(AxisScaleBase):
540-
"""Tool to toggle between linear and logarithmic scales on the Y axis"""
541-
542-
description = 'Toogle Scale Y axis'
543-
default_keymap = rcParams['keymap.yscale']
544-
545-
def set_scale(self, ax, scale):
546-
ax.set_yscale(scale)
547-
548-
583+
@register_tool("xscale")
549584
class ToolXScale(AxisScaleBase):
550585
"""Tool to toggle between linear and logarithmic scales on the X axis"""
551586

@@ -556,6 +591,18 @@ def set_scale(self, ax, scale):
556591
ax.set_xscale(scale)
557592

558593

594+
@register_tool("yscale")
595+
class ToolYScale(AxisScaleBase):
596+
"""Tool to toggle between linear and logarithmic scales on the Y axis"""
597+
598+
description = 'Toogle Scale Y axis'
599+
default_keymap = rcParams['keymap.yscale']
600+
601+
def set_scale(self, ax, scale):
602+
ax.set_yscale(scale)
603+
604+
605+
@register_tool("viewpos")
559606
class ToolViewsPositions(ToolBase):
560607
"""
561608
Auxiliary Tool to handle changes in views and positions
@@ -714,12 +761,13 @@ class ViewsPositionsBase(ToolBase):
714761
_on_trigger = None
715762

716763
def trigger(self, sender, event, data=None):
717-
self.toolmanager.get_tool(_views_positions).add_figure(self.figure)
718-
getattr(self.toolmanager.get_tool(_views_positions),
764+
self.toolmanager.get_tool("viewpos").add_figure(self.figure)
765+
getattr(self.toolmanager.get_tool("viewpos"),
719766
self._on_trigger)()
720-
self.toolmanager.get_tool(_views_positions).update_view()
767+
self.toolmanager.get_tool("viewpos").update_view()
721768

722769

770+
@register_tool("home")
723771
class ToolHome(ViewsPositionsBase):
724772
"""Restore the original view lim"""
725773

@@ -729,6 +777,7 @@ class ToolHome(ViewsPositionsBase):
729777
_on_trigger = 'home'
730778

731779

780+
@register_tool("back")
732781
class ToolBack(ViewsPositionsBase):
733782
"""Move back up the view lim stack"""
734783

@@ -738,6 +787,7 @@ class ToolBack(ViewsPositionsBase):
738787
_on_trigger = 'back'
739788

740789

790+
@register_tool("forward")
741791
class ToolForward(ViewsPositionsBase):
742792
"""Move forward in the view lim stack"""
743793

@@ -747,13 +797,15 @@ class ToolForward(ViewsPositionsBase):
747797
_on_trigger = 'forward'
748798

749799

800+
@register_tool("subplots")
750801
class ConfigureSubplotsBase(ToolBase):
751802
"""Base tool for the configuration of subplots"""
752803

753804
description = 'Configure subplots'
754805
image = 'subplots'
755806

756807

808+
@register_tool("save")
757809
class SaveFigureBase(ToolBase):
758810
"""Base tool for figure saving"""
759811

@@ -794,7 +846,7 @@ def disable(self, event):
794846
self.figure.canvas.mpl_disconnect(self._idScroll)
795847

796848
def trigger(self, sender, event, data=None):
797-
self.toolmanager.get_tool(_views_positions).add_figure(self.figure)
849+
self.toolmanager.get_tool("viewpos").add_figure(self.figure)
798850
ToolToggleBase.trigger(self, sender, event, data)
799851

800852
def scroll_zoom(self, event):
@@ -818,14 +870,15 @@ def scroll_zoom(self, event):
818870
# If last scroll was done within the timing threshold, delete the
819871
# previous view
820872
if (time.time()-self.lastscroll) < self.scrollthresh:
821-
self.toolmanager.get_tool(_views_positions).back()
873+
self.toolmanager.get_tool("viewpos").back()
822874

823875
self.figure.canvas.draw_idle() # force re-draw
824876

825877
self.lastscroll = time.time()
826-
self.toolmanager.get_tool(_views_positions).push_current()
878+
self.toolmanager.get_tool("viewpos").push_current()
827879

828880

881+
@register_tool("zoom")
829882
class ToolZoom(ZoomPanBase):
830883
"""Zoom to rectangle"""
831884

@@ -843,7 +896,7 @@ def _cancel_action(self):
843896
for zoom_id in self._ids_zoom:
844897
self.figure.canvas.mpl_disconnect(zoom_id)
845898
self.toolmanager.trigger_tool('rubberband', self)
846-
self.toolmanager.get_tool(_views_positions).refresh_locators()
899+
self.toolmanager.get_tool("viewpos").refresh_locators()
847900
self._xypress = None
848901
self._button_pressed = None
849902
self._ids_zoom = []
@@ -948,10 +1001,11 @@ def _release(self, event):
9481001
self._zoom_mode, twinx, twiny)
9491002

9501003
self._zoom_mode = None
951-
self.toolmanager.get_tool(_views_positions).push_current()
1004+
self.toolmanager.get_tool("viewpos").push_current()
9521005
self._cancel_action()
9531006

9541007

1008+
@register_tool("pan")
9551009
class ToolPan(ZoomPanBase):
9561010
"""Pan axes with left mouse, zoom with right"""
9571011

@@ -970,7 +1024,7 @@ def _cancel_action(self):
9701024
self._xypress = []
9711025
self.figure.canvas.mpl_disconnect(self._idDrag)
9721026
self.toolmanager.messagelock.release(self)
973-
self.toolmanager.get_tool(_views_positions).refresh_locators()
1027+
self.toolmanager.get_tool("viewpos").refresh_locators()
9741028

9751029
def _press(self, event):
9761030
if event.button == 1:
@@ -1007,7 +1061,7 @@ def _release(self, event):
10071061
self._cancel_action()
10081062
return
10091063

1010-
self.toolmanager.get_tool(_views_positions).push_current()
1064+
self.toolmanager.get_tool("viewpos").push_current()
10111065
self._cancel_action()
10121066

10131067
def _mouse_move(self, event):
@@ -1018,24 +1072,11 @@ def _mouse_move(self, event):
10181072
self.toolmanager.canvas.draw_idle()
10191073

10201074

1021-
default_tools = {'home': ToolHome, 'back': ToolBack, 'forward': ToolForward,
1022-
'zoom': ToolZoom, 'pan': ToolPan,
1023-
'subplots': 'ToolConfigureSubplots',
1024-
'save': 'ToolSaveFigure',
1025-
'grid': ToolGrid,
1026-
'grid_minor': ToolMinorGrid,
1027-
'fullscreen': ToolFullScreen,
1028-
'quit': ToolQuit,
1029-
'quit_all': ToolQuitAll,
1030-
'allnav': ToolEnableAllNavigation,
1031-
'nav': ToolEnableNavigation,
1032-
'xscale': ToolXScale,
1033-
'yscale': ToolYScale,
1034-
'position': ToolCursorPosition,
1035-
_views_positions: ToolViewsPositions,
1036-
'cursor': 'ToolSetCursor',
1037-
'rubberband': 'ToolRubberband',
1038-
}
1075+
default_tools = [
1076+
'home', 'back', 'forward', 'zoom', 'pan', 'subplots', 'save',
1077+
'grid', 'grid_minor', 'fullscreen', 'quit', 'quit_all', 'allnav', 'nav',
1078+
'xscale', 'yscale', 'position', 'viewpos', 'cursor', 'rubberband',
1079+
]
10391080
"""Default tools"""
10401081

10411082
default_toolbar_tools = [['navigation', ['home', 'back', 'forward']],
@@ -1052,13 +1093,12 @@ def add_tools_to_manager(toolmanager, tools=default_tools):
10521093
----------
10531094
toolmanager: ToolManager
10541095
`backend_managers.ToolManager` object that will get the tools added
1055-
tools : {str: class_like}, optional
1056-
The tools to add in a {name: tool} dict, see `add_tool` for more
1057-
info.
1096+
tools : List[str], optional
1097+
The tools to add.
10581098
"""
10591099

1060-
for name, tool in six.iteritems(tools):
1061-
toolmanager.add_tool(name, tool)
1100+
for tool_name in tools:
1101+
toolmanager.add_tool(tool_name)
10621102

10631103

10641104
def add_tools_to_container(container, tools=default_toolbar_tools):

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy