Skip to content

Explicit tool registration. #9941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions examples/user_interfaces/toolmanager_sgskip.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,17 @@
matplotlib.use('GTK3Cairo')
matplotlib.rcParams['toolbar'] = 'toolmanager'
import matplotlib.pyplot as plt
from matplotlib.backend_tools import ToolBase, ToolToggleBase
from matplotlib.backend_tools import ToolBase, ToolToggleBase, register_tool


class ListTools(ToolBase):
class ListTool(ToolBase):
'''List all the tools controlled by the `ToolManager`'''
# keyboard shortcut
name = "List"
default_keymap = 'm'
description = 'List Tools'

def trigger(self, *args, **kwargs):
def trigger(self, event):
print('_' * 80)
print("{0:12} {1:45} {2}".format(
'Name (id)', 'Tool description', 'Keymap'))
Expand All @@ -50,6 +51,7 @@ def trigger(self, *args, **kwargs):

class GroupHideTool(ToolToggleBase):
'''Show lines with a given gid'''
name = "Show"
default_keymap = 'G'
description = 'Show by gid'
default_toggled = True
Expand All @@ -58,10 +60,10 @@ def __init__(self, *args, **kwargs):
self.gid = kwargs.pop('gid')
ToolToggleBase.__init__(self, *args, **kwargs)

def enable(self, *args):
def enable(self, event):
self.set_lines_visibility(True)

def disable(self, *args):
def disable(self, event):
self.set_lines_visibility(False)

def set_lines_visibility(self, state):
Expand All @@ -79,9 +81,8 @@ def set_lines_visibility(self, state):
plt.plot([3, 2, 1], gid='mygroup')

# Add the custom tools that we created
fig.canvas.manager.toolmanager.add_tool('List', ListTools)
fig.canvas.manager.toolmanager.add_tool('Show', GroupHideTool, gid='mygroup')

fig.canvas.manager.toolmanager.add_tool(ListTool)
fig.canvas.manager.toolmanager.add_tool(GroupHideTool, gid='mygroup')

# Add an existing tool to new group `foo`.
# It can be added as many times as we want
Expand Down
15 changes: 9 additions & 6 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
import numpy as np

from matplotlib import (
backend_tools as tools, cbook, colors, textpath, tight_bbox, transforms,
widgets, get_backend, is_interactive, rcParams)
cbook, colors, textpath, tight_bbox, transforms, widgets, get_backend,
is_interactive, rcParams)
from matplotlib._pylab_helpers import Gcf
from matplotlib.transforms import Bbox, TransformedBbox, Affine2D
from matplotlib.path import Path
Expand Down Expand Up @@ -91,6 +91,12 @@
}


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


def register_backend(format, backend, description=None):
"""
Register a backend for saving to a given file format.
Expand Down Expand Up @@ -2704,9 +2710,6 @@ def set_window_title(self, title):
"""


cursors = tools.cursors


class NavigationToolbar2(object):
"""
Base class for the navigation cursor, version 2
Expand Down Expand Up @@ -3282,7 +3285,7 @@ def trigger_tool(self, name):
name : String
Name (id) of the tool triggered from within the container
"""
self.toolmanager.trigger_tool(name, sender=self)
self.toolmanager.trigger_tool(name)

def add_toolitem(self, name, group, position, image, description, toggle):
"""
Expand Down
93 changes: 29 additions & 64 deletions lib/matplotlib/backend_managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,15 @@

class ToolEvent(object):
"""Event for tool manipulation (add/remove)"""
def __init__(self, name, sender, tool, data=None):
def __init__(self, name, tool):
self.name = name
self.sender = sender
self.tool = tool
self.data = data


class ToolTriggerEvent(ToolEvent):
"""Event to inform that a tool has been triggered"""
def __init__(self, name, sender, tool, canvasevent=None, data=None):
ToolEvent.__init__(self, name, sender, tool, data)
def __init__(self, name, tool, canvasevent=None):
ToolEvent.__init__(self, name, tool)
self.canvasevent = canvasevent


Expand All @@ -37,9 +35,8 @@ class ToolManagerMessageEvent(object):

Messages usually get displayed to the user by the toolbar
"""
def __init__(self, name, sender, message):
def __init__(self, name, message):
self.name = name
self.sender = sender
self.message = message


Expand Down Expand Up @@ -150,13 +147,10 @@ def toolmanager_disconnect(self, cid):
"""
return self._callbacks.disconnect(cid)

def message_event(self, message, sender=None):
def message_event(self, message):
""" Emit a `ToolManagerMessageEvent`"""
if sender is None:
sender = self

s = 'tool_message_event'
event = ToolManagerMessageEvent(s, sender, message)
event = ToolManagerMessageEvent(s, message)
self._callbacks.process(s, event)

@property
Expand Down Expand Up @@ -229,12 +223,12 @@ def remove_tool(self, name):
self._remove_keys(name)

s = 'tool_removed_event'
event = ToolEvent(s, self, tool)
event = ToolEvent(s, tool)
self._callbacks.process(s, event)

del self._tools[name]

def add_tool(self, name, tool, *args, **kwargs):
def add_tool(self, tool_cls_or_name, *args, **kwargs):
"""
Add *tool* to `ToolManager`

Expand All @@ -244,8 +238,8 @@ def add_tool(self, name, tool, *args, **kwargs):

Parameters
----------
name : str
Name of the tool, treated as the ID, has to be unique
tool_cls_or_name : callable or name
Class or name of the tool, treated as the ID, has to be unique
tool : class_like, i.e. str or type
Reference to find the class of the Tool to added.

Expand All @@ -258,16 +252,21 @@ def add_tool(self, name, tool, *args, **kwargs):
matplotlib.backend_tools.ToolBase : The base class for tools.
"""

tool_cls = self._get_cls_to_instantiate(tool)
if not tool_cls:
raise ValueError('Impossible to find class for %s' % str(tool))
if callable(tool_cls_or_name):
tool_cls = tool_cls_or_name
elif isinstance(tool_cls_or_name, six.string_types):
tool_cls = tools.get_tool(tool_cls_or_name, type(self.canvas))
else:
raise TypeError(
"'tool_cls_or_name' must be a callable or a tool name")
name = tool_cls.name

if name in self._tools:
warnings.warn('A "Tool class" with the same name already exists, '
'not added')
return self._tools[name]

tool_obj = tool_cls(self, name, *args, **kwargs)
tool_obj = tool_cls(self, *args, **kwargs)
self._tools[name] = tool_obj

if tool_cls.default_keymap is not None:
Expand All @@ -284,31 +283,27 @@ def add_tool(self, name, tool, *args, **kwargs):

# If initially toggled
if tool_obj.toggled:
self._handle_toggle(tool_obj, None, None, None)
self._handle_toggle(tool_obj, None)
tool_obj.set_figure(self.figure)

self._tool_added_event(tool_obj)
return tool_obj

def _tool_added_event(self, tool):
s = 'tool_added_event'
event = ToolEvent(s, self, tool)
event = ToolEvent(s, tool)
self._callbacks.process(s, event)

def _handle_toggle(self, tool, sender, canvasevent, data):
def _handle_toggle(self, tool, canvasevent):
"""
Toggle tools, need to untoggle prior to using other Toggle tool
Called from trigger_tool

Parameters
----------
tool: Tool object
sender: object
Object that wishes to trigger the tool
canvasevent : Event
Original Canvas event or None
data : Object
Extra data to pass to the tool when triggering
"""

radio_group = tool.radio_group
Expand All @@ -331,62 +326,32 @@ def _handle_toggle(self, tool, sender, canvasevent, data):
# Other tool in the radio_group is toggled
else:
# Untoggle previously toggled tool
self.trigger_tool(self._toggled[radio_group],
self,
canvasevent,
data)
self.trigger_tool(self._toggled[radio_group], canvasevent)
toggled = tool.name

# Keep track of the toggled tool in the radio_group
self._toggled[radio_group] = toggled

def _get_cls_to_instantiate(self, callback_class):
# Find the class that corresponds to the tool
if isinstance(callback_class, six.string_types):
# FIXME: make more complete searching structure
if callback_class in globals():
callback_class = globals()[callback_class]
else:
mod = 'backend_tools'
current_module = __import__(mod,
globals(), locals(), [mod], 1)

callback_class = getattr(current_module, callback_class, False)
if callable(callback_class):
return callback_class
else:
return None

def trigger_tool(self, name, sender=None, canvasevent=None,
data=None):
def trigger_tool(self, name, canvasevent=None):
"""
Trigger a tool and emit the tool_trigger_[name] event

Parameters
----------
name : string
Name of the tool
sender: object
Object that wishes to trigger the tool
canvasevent : Event
Original Canvas event or None
data : Object
Extra data to pass to the tool when triggering
"""
tool = self.get_tool(name)
if tool is None:
return

if sender is None:
sender = self

self._trigger_tool(name, sender, canvasevent, data)

self._trigger_tool(name, canvasevent)
s = 'tool_trigger_%s' % name
event = ToolTriggerEvent(s, sender, tool, canvasevent, data)
event = ToolTriggerEvent(s, tool, canvasevent)
self._callbacks.process(s, event)

def _trigger_tool(self, name, sender=None, canvasevent=None, data=None):
def _trigger_tool(self, name, canvasevent=None):
"""
Trigger on a tool

Expand All @@ -395,11 +360,11 @@ def _trigger_tool(self, name, sender=None, canvasevent=None, data=None):
tool = self.get_tool(name)

if isinstance(tool, tools.ToolToggleBase):
self._handle_toggle(tool, sender, canvasevent, data)
self._handle_toggle(tool, canvasevent)

# Important!!!
# This is where the Tool object gets triggered
tool.trigger(sender, canvasevent, data)
tool.trigger(canvasevent)

def _key_press(self, event):
if event.key is None or self.keypresslock.locked():
Expand Down
Loading