8000 MEP22: Navigation by events by fariza · Pull Request #3652 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

MEP22: Navigation by events #3652

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

Merged
merged 69 commits into from
Apr 9, 2015
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
8cceed4
navigation and toolbar coexistence
fariza Jan 23, 2014
3118a5a
mod keypress in figuremanager
fariza Jan 23, 2014
b4d5fcf
helper methods in toolbar and navigation
fariza Jan 24, 2014
1e8af47
Adding doc to base methods
fariza Jan 24, 2014
622cb95
property for active_toggle
fariza Jan 26, 2014
d1a9de4
simulate click
fariza Jan 27, 2014
3f89d52
activate renamed to trigger
fariza Jan 28, 2014
4f3c10b
toggle tools using enable/disable from its trigger method
fariza Jan 29, 2014
6065daa
simplifying _handle_toggle
fariza Jan 29, 2014
f6a2f19
reducing number of locks
fariza Jan 29, 2014
05db3b6
changing toggle and persistent attributes for issubclass
fariza Feb 4, 2014
c08fe56
bug in combined key press
fariza Feb 4, 2014
b207a72
untoggle zoom and pan from keypress while toggled
fariza Feb 4, 2014
9266447
classmethods for default tools modification
fariza Feb 6, 2014
a53419a
adding zaxis and some pep8
fariza May 1, 2014
704c717
removing legacy method dynamic update
fariza May 6, 2014
5056729
tk backend
fariza May 6, 2014
e6a4e1e
example working with Tk
fariza May 6, 2014
8942c47
duplicate code in keymap tool initialization
fariza Jul 24, 2014
022de6f
grammar corrections
fariza Jul 24, 2014
2c9a195
moving views and positions to tools
fariza Jul 24, 2014
cafe668
The views positions mixin automatically adds the clear as axobserver
fariza Jul 25, 2014
224f745
bug when navigation was not defined
fariza Jul 25, 2014
94c711e
Small refactor so that we first initiate the Navigation (ToolManager)…
OceanWolf Jul 28, 2014
67257e7
Moved default_tool initilisation to FigureManagerBase and cleaned.
OceanWolf Jul 29, 2014
ffa65d6
Temporary fix to backends
OceanWolf Jul 29, 2014
6739ee0
removing persistent tools
fariza Sep 3, 2014
d18206f
removing unregister
fariza Sep 4, 2014
34a52c8
change cursor inmediately after toggle
fariza Sep 5, 2014
c2da483
removing intoolbar
fariza Oct 15, 2014
44a9b0e
events working
fariza Oct 16, 2014
a2ed47f
using pydispatch
fariza Oct 17, 2014
0665890
using navigation as signal handler
fariza Oct 20, 2014
411e6e2
removing view positions singleton
fariza Oct 20, 2014
d484ebd
moving set_cursor completely out of navigation
fariza Oct 27, 2014
75bf97b
removing unused event class
fariza Nov 10, 2014
6cc040b
underscore in tool_trigger-xxx
fariza Nov 10, 2014
0ff5997
adding radio_group for toggle tools
fariza Nov 14, 2014
af6734f
scroll to zoom in zoom/pan tools
fariza Nov 28, 2014
78513d2
remove ToolAddedEvent incorporating the functionality into toolevent
fariza Dec 5, 2014
377ff54
eliminating repeated loop
fariza Jan 5, 2015
7dbbf58
replace draw by draw_idle in tools
fariza Jan 21, 2015
dd66b57
rename mpl_connect
fariza Jan 21, 2015
67a414f
cleaning navigation and toolbar dependencies
fariza Feb 4, 2015
e415d8d
Made NavigationBase.get_tool() more useful.
OceanWolf Feb 11, 2015
1213086
Refactored Toolbar out of NavigationBase
OceanWolf Feb 12, 2015
ba61dec
Some short cuts for adding tools
OceanWolf Feb 16, 2015
9f2ee2b
Lots of fixes
OceanWolf Feb 18, 2015
9da2b13
Rename ToolbarBase -> ToolContainerBase
OceanWolf Feb 18, 2015
110253f
Statusbar
OceanWolf Feb 20, 2015
e2804ea
tool group position
fariza Feb 26, 2015
9a64b7e
docstrings and small corrections by WeatherGod
fariza Mar 23, 2015
64f947f
tkbackend updated
fariza Mar 31, 2015
e8cd5d5
tacaswell comments aprl 1
fariza Apr 1, 2015
4bbcf4e
renaming tool_trigger_event
fariza Apr 1, 2015
73a2661
add_tools moved out of base classes
fariza Apr 1, 2015
1b83628
figure.setter in tools
fariza Apr 1, 2015
e4edd23
rename tools to default_tools to avoid confusion
fariza Apr 1, 2015
d4ac2fb
docstring helper add_tools methods
fariza Apr 1, 2015
a7640ef
rename navigation to toolmanager
fariza Apr 2, 2015
48a6971
tkagg updated for toolmanager and tool groups
fariza Apr 2, 2015
8dafe09
doc and minor code organization
fariza Apr 2, 2015
a0695d0
whats new
fariza Apr 3, 2015
328b169
missing object from class declaration
fariza Apr 3, 2015
aac4744
remove comments and docstrings
fariza Apr 3, 2015
f09b9ef
import with original name backend_tools
fariza Apr 3, 2015
def3a52
rename 2 -> to, example without gtk only code
fariza Apr 7, 2015
9ee7e25
zoom pan buttons order
fariza Apr 7, 2015
5eae4e1
matplotlib.rcParams['toolbar'] == 'None' starts toolmanager but not t…
fariza Apr 7, 2015
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
Prev Previous commit
Next Next commit
remove ToolAddedEvent incorporating the functionality into toolevent
  • Loading branch information
fariza committed Apr 7, 2015
commit 78513d24a9f9895ece7affacd56432b4f2b7fec8
77 changes: 34 additions & 43 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -3227,32 +3227,24 @@ def set_history_buttons(self):

class ToolEvent(object):
"""Event for tool manipulation (add/remove)"""
def __init__(self, name, sender, tool):
def __init__(self, name, sender, tool, data=None):
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)
ToolEvent.__init__(self, name, sender, tool, data)
self.canvasevent = canvasevent
self.data = data


class ToolAddedEvent(ToolEvent):
"""Event triggered when a tool is added"""
def __init__(self, name, sender, tool, group, position):
ToolEvent.__init__(self, name, sender, tool)
self.group = group
self.position = position


class NavigationMessageEvent(object):
"""Event carring messages from navigation
"""Event carrying messages from navigation
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-line docstrings should have the triple-quote by themselves


Messages are generaly displayed to the user by the toolbar
Messages usually get displayed to the user by the toolbar
"""
def __init__(self, name, sender, message):
self.name = name
Expand Down Expand Up @@ -3339,7 +3331,7 @@ def active_toggle(self):
return self._toggled

def get_tool_keymap(self, name):
"""Get the keymap associated with a tool
"""Get the keymap associated with the specified tool

Parameters
----------
Expand All @@ -3360,13 +3352,13 @@ def _remove_keys(self, name):
del self._keys[k]

def set_tool_keymap(self, name, *keys):
"""Set the keymap associated with a tool
"""Set the keymap to associate with the specified tool

Parameters
----------
name : string
Name of the Tool
keys : keys to associated with the Tool
keys : keys to associate with the Tool
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, a bit confused here. Why keys as *args? You expect someone to do both

set_tool_keymap('zoom', 'o', 'z')
set_tool_keymap('zoom', 'o,z')?

Just doing another PR here, so I wanted to check, the docstring not too clear here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, I just followed the convention for default keymaps.
In rcsetup.py you have for example 'keymap.home': [['h', 'r', 'home'], validate_stringlist]

"""

if name not in self._tools:
Expand Down Expand Up @@ -3440,7 +3432,7 @@ def add_tool(self, name, tool, group=None, position=None):
group: String
Group to position the tool in
position : int or None (default)
Position within its group in the toolbar, if None, is positioned at the end
Position within its group in the toolbar, if None, it goes at the end
"""

tool_cls = self._get_cls_to_instantiate(tool)
Expand All @@ -3457,7 +3449,7 @@ def add_tool(self, name, tool, group=None, position=None):
if tool_cls.keymap is not None:
self.set_tool_keymap(name, tool_cls.keymap)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't this be self._tools[name].keymap ? Might as well allow for per-instance key binding.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The one who makes the sorting (by keymap) of the keypress events is NavigationBase.
It is easier to handleNavigationBase._keys dictionnary than looping throught all the tools finding their keymaps when an event occurs.
What we can do, if you insist in per-instance key binding, is to modify set_tool_keymap to set the tool keymap attribute at the same time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh? per-instance key binding happens, this just sets the default keymap.

@fariza I think you should rename ToolBase.keymap to ToolBase.default_keymap as I think that would make it a lot clearer!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite follow this yet..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OceanWolf agreed and done

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tacaswell set_tool_keymap(name, keymap) does per-instance key binding, it sets the keymap of the instance identified by name to the specified keymap. Here it takes the default keymap (a class level property) and binds it to the instance. Does that make it clearer for you?


# For toggle tools init the radio_grop in self._toggled
# For toggle tools init the radio_group in self._toggled
if getattr(tool_cls, 'toggled', False) is not False:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not if not getattr(tool_cls, 'toggled', False):? Are you expecting non-booleans as possible values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to a proper ToggleTool verification

# None group is not mutually exclusive, a set is used to keep track
# of all toggled tools in this group
Expand All @@ -3470,15 +3462,15 @@ def add_tool(self, name, tool, group=None, position=None):

def _tool_added_event(self, tool, group, position):
s = 'tool_added_event'
event = ToolAddedEvent(s, self,
tool,
group,
position)
event = ToolEvent(s,
self,
tool,
data={'group': group, 'position': position})
self._callbacks.process(s, event)

def _handle_toggle(self, tool, sender, canvasevent, data):
# Toggle tools, need to be untoggled before other Toggle tool is used
# This is called from tool_trigger_event
# Toggle tools, need to untoggle prior to using other Toggle tool
# Called from tool_trigger_event
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turn this into a docstring


radio_group = tool.radio_group
# radio_group None is not mutually exclusive
Expand All @@ -3490,8 +3482,7 @@ def _handle_toggle(self, tool, sender, canvasevent, data):
self._toggled[None].add(tool.name)
return

# If it is the same tool that is toggled in the radio_group
# untoggle it
# If the tool already has a toggled state, untoggle it
if self._toggled[radio_group] == tool.name:
toggled = None
# If no tool was toggled in the radio_group
Expand Down Expand Up @@ -3536,7 +3527,7 @@ def tool_trigger_event(self, name, sender=None, canvasevent=None,
name : string
Name of the tool
sender: object
Object that wish to trigger the tool
Object that wishes to trigger the tool
canvasevent : Event
Original Canvas event or None
data : Object
Expand Down Expand Up @@ -3567,7 +3558,7 @@ def _trigger_tool(self, name, sender=None, canvasevent=None, data=None):
self._handle_toggle(tool, sender, canvasevent, data)

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

def _key_press(self, event):
Expand Down Expand Up @@ -3616,26 +3607,26 @@ def __init__(self, manager):
self._remove_tool_cbk)

def _message_cbk(self, event):
"""Captures the 'tool_message_event' to set message on the toolbar"""
"""Captures the 'tool_message_event' to set the message on the toolbar"""
self.set_message(event.message)

def _tool_triggered_cbk(self, event):
"""Captures the 'tool-trigger-toolname
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be "tool_trigger_[name]" to be consistent with documentation elsewhere.


This is only used for toggled tools
This only gets used for toggled tools
"""
if event.sender is self:
return

self.toggle_toolitem(event.tool.name)

def _add_tool_cbk(self, event):
"""Captures 'tool_added_event' and add the tool to the toolbar"""
"""Captures 'tool_added_event' and adds the tool to the toolbar"""
image = self._get_image_filename(event.tool.image)
toggle = getattr(event.tool, 'toggled', None) is not None
self.add_toolitem(event.tool.name,
event.group,
event.position,
event.data['group'],
event.data['position'],
image,
event.tool.description,
toggle)
Expand All @@ -3644,11 +3635,11 @@ def _add_tool_cbk(self, event):
self._tool_triggered_cbk)

def _remove_tool_cbk(self, event):
"""Captures the 'tool_removed_event' signal and remove the tool"""
"""Captures the 'tool_removed_event' signal and removes the tool"""
self.remove_toolitem(event.tool.name)

def _get_image_filename(self, image):
""""Base on the image name find the corresponding image"""
"""Find the image based on its name"""
# TODO: better search for images, they are not always in the
# datapath
basedir = os.path.join(rcParams['datapath'], 'images')
Expand All @@ -3664,28 +3655,28 @@ def trigger_tool(self, name):
Parameters
----------
name : String
Name(id) of the tool that was triggered in the toolbar
Name(id) of the tool triggered from within the toolbar

"""
self.navigation.tool_trigger_event(name, sender=self)

def add_toolitem(self, name, group, position, image, description, toggle):
"""Add a toolitem to the toolbar

This method has to be implemented per backend
This method must get implemented per backend

The callback associated with the button click event,
must be **EXACTLY** `self.trigger_tool(name)`

Parameters
----------
name : string
Name of the tool to add, this is used as ID and as default label
of the buttons
Name of the tool to add, this gets used as the tool's ID and as the
default label of the buttons
group : String
Name of the group that the tool belongs to
Name of the group that this tool belongs to
position : Int
Position of the tool whthin its group if -1 at the End
Position of the tool within its group, if -1 it goes at the End
image_file : String
Filename of the image for the button or `None`
description : String
Expand Down Expand Up @@ -3723,9 +3714,9 @@ def toggle_toolitem(self, name):
def remove_toolitem(self, name):
"""Remove a toolitem from the `Toolbar`

This method has to be implemented per backend
This method must get implemented per backend

Called when `tool_removed_event` is emited by `NavigationBase`
Called when `NavigationBase` emits a `tool_removed_event`

Parameters
----------
Expand Down
19 changes: 9 additions & 10 deletions lib/matplotlib/backend_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,15 @@ def trigger(self, sender, event, data=None):
def enable(self, event=None):
"""Enable the toggle tool

This method is called dby `trigger` when the `toggled` is False
`trigger` calls this method when `toggled` is False
"""

pass

def disable(self, event=None):
"""Disable the toggle tool

This method is called by `trigger` when the `toggled` is True.
`trigger` call this methond when `toggled` is True.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"methond" --> "method"


This can happen in different circumstances

Expand All @@ -174,7 +174,7 @@ class SetCursorBase(ToolBase):
"""Change to the current cursor while inaxes

This tool, keeps track of all `ToolToggleBase` derived tools, and calls
set_cursor when one of these tools is triggered
set_cursor when a tool gets triggered
"""
def __init__(self, *args, **kwargs):
ToolBase.__init__(self, *args, **kwargs)
Expand Down Expand Up @@ -251,7 +251,6 @@ def send_message(self, event):
message = ' '

if event.inaxes and event.inaxes.get_navigate():

try:
s = event.inaxes.format_coord(event.xdata, event.ydata)
except (ValueError, OverflowError):
Expand All @@ -275,14 +274,14 @@ def trigger(self, sender, event, data):
def draw_rubberband(self, *data):
"""Draw rubberband

This method has to be implemented per backend
This method must get implemented per backend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it must, then it should raise NotImplementedError.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should take advantage of ABC's? I haven't worked with them personally, but my understanding is that it can let you know if a dev failed to implement all the required methods.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to limit new dependencies that weren't used somewhere else

"""
pass

def remove_rubberband(self):
"""Remove rubberband

This method has to be implemented per backend
This method must get implemented per backend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, with the must.

"""
pass

Expand Down Expand Up @@ -383,7 +382,7 @@ def disable(self, event):


class ToolYScale(AxisScaleBase):
"""Tool to toggle between linear and logarithmic the Y axis"""
"""Tool to toggle between linear and logarithmic scales on the Y axis"""

description = 'Toogle Scale Y axis'
keymap = rcParams['keymap.yscale']
Expand All @@ -393,7 +392,7 @@ def set_scale(self, ax, scale):


class ToolXScale(AxisScaleBase):
"""Tool to toggle between linear and logarithmic the X axis"""
"""Tool to toggle between linear and logarithmic scales on the X axis"""

description = 'Toogle Scale X axis'
keymap = rcParams['keymap.xscale']
Expand All @@ -405,8 +404,8 @@ def set_scale(self, ax, scale):
class ToolViewsPositions(ToolBase):
"""Auxiliary Tool to handle changes in views and positions

Runs in the background and is used by all the tools that
need to access the record of views and positions of the figure
Runs in the background and should get used by all the tools that
need to access the figure's history of views and positions, e.g.

* `ToolZoom`
* `ToolPan`
Expand Down
0