8000 MEP22 Navigation toolbar coexistence TODELETE by fariza · Pull Request #2759 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

MEP22 Navigation toolbar coexistence TODELETE #2759

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 38 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
dda0cdc
navigation and toolbar coexistence
fariza Jan 23, 2014
10f5dc7
mod keypress in figuremanager
fariza Jan 23, 2014
08a6020
extra files
fariza Jan 23, 2014
c6c0ad3
helper methods in toolbar and navigation
fariza Jan 24, 2014
1fc29fa
Adding doc to base methods
fariza Jan 24, 2014
979875e
property for active_toggle
fariza Jan 26, 2014
c5c4f0f
simulate click
fariza Jan 27, 2014
97dfda7
pep8 backend_tools
fariza Jan 27, 2014
6b647ad
activate renamed to trigger
fariza Jan 28, 2014
99667aa
toggle tools using enable/disable from its trigger method
fariza Jan 29, 2014
6c19579
simplifying _handle_toggle
fariza Jan 29, 2014
0495aac
reducing number of locks
fariza Jan 29, 2014
fb46fc1
pep8 correction
fariza Jan 29, 2014
d49c431
changing toggle and persistent attributes for issubclass
fariza Feb 4, 2014
5ba6210
bug in combined key press
fariza Feb 4, 2014
7ca8626
untoggle zoom and pan from keypress while toggled
fariza Feb 4, 2014
dcc0f16
classmethods for default tools modification
fariza Feb 6, 2014
bc703e0
six fixes
fariza Apr 24, 2014
68dc711
adding zaxis and some pep8
fariza May 1, 2014
bb9f1c7
removing legacy method dynamic update
fariza May 6, 2014
2c2e649
tk backend
fariza May 6, 2014
a99367f
pep8
fariza May 6, 2014
3d1be34
example working with Tk
fariza May 6, 2014
afdd34c
cleanup
fariza May 7, 2014
5b49c7a
duplicate code in keymap tool initialization
fariza Jul 24, 2014
773db88
grammar corrections
fariza Jul 24, 2014
2ca6926
moving views and positions to tools
fariza Jul 24, 2014
661417d
The views positions mixin automatically adds the clear as axobserver
fariza Jul 25, 2014
90ab64f
bug when navigation was not defined
fariza Jul 25, 2014
55dd149
Small refactor so that we first initiate the Navigation (ToolManager)…
OceanWolf Jul 28, 2014
15ac091
Update for Sphinx documentation
OceanWolf Jul 28, 2014
8cd241c
Moved default_tool initilisation to FigureManagerBase and cleaned.
OceanWolf Jul 29, 2014
39f5b74
Fix navigation
OceanWolf Jul 29, 2014
b20dade
Temporary fix to backends
OceanWolf Jul 29, 2014
ff94301
Merge pull request #1 from OceanWolf/navigation-toolbar-coexistence-e…
fariza Jul 29, 2014
2cb4501
removing persistent tools
fariza Sep 3, 2014
9d3c977
removing unregister
fariza Sep 4, 2014
6e0b7e6
change cursor inmediately after toggle
fariza Sep 5, 2014
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
cleanup
  • Loading branch information
fariza committed May 7, 2014
commit afdd34c9d9116fec40f71e148f69bc05900c7c76
26 changes: 16 additions & 10 deletions examples/user_interfaces/navigation.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,31 @@
class ListTools(ToolBase):
# keyboard shortcut
keymap = 'm'
# Name used as id, must be unique between tools of the same navigation
name = 'List'
description = 'List Tools'
# Where to put it in the toolbar, -1 = at the end, None = Not in toolbar
position = -1

def trigger(self, event):
# The most important attributes are navigation and figure
self.navigation.list_tools()
tools = self.navigation.get_tools()

print ('_' * 80)
print ("{0:12} {1:45} {2}".format('Name (id)',
'Tool description',
'Keymap'))
print ('_' * 80)
for name in sorted(tools.keys()):
keys = ', '.join(sorted(tools[name]['keymap']))
print ("{0:12} {1:45} {2}".format(name,
tools[name]['description'],
keys))
print ('_' * 80)


# A simple example of copy canvas
# ref: at https://github.com/matplotlib/matplotlib/issues/1987
class CopyToolGTK3(ToolBase):
keymap = 'ctrl+c'
name = 'Copy'
description = 'Copy canvas'
# It is not added to the toolbar as a button
position = None
intoolbar = False

def trigger(self, event):
from gi.repository import Gtk, Gdk
Expand All @@ -43,9 +49,9 @@ def trigger(self, event):
plt.plot([1, 2, 3])

# Add the custom tools that we created
fig.canvas.manager.navigation.add_tool(ListTools)
fig.canvas.manager.navigation.add_tool('List', ListTools)
if matplotlib.rcParams['backend'] == 'GTK3Cairo':
fig.canvas.manager.navigation.add_tool(CopyToolGTK3)
fig.canvas.manager.navigation.add_tool('copy', CopyToolGTK3)

# Just for fun, lets remove the back button
fig.canvas.manager.navigation.remove_tool('Back')
Expand Down
110 changes: 35 additions & 75 deletions lib/matplotlib/backend_bases.py
B41A
Original file line number Diff line number Diff line change
Expand Up @@ -3188,41 +3188,27 @@ class NavigationBase(object):

Attributes
Copy link
Member

Choose a reason for hiding this comment

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

white space issue?

----------
canvas : `FigureCanvas` instance
manager : `FigureManager` instance
toolbar : `Toolbar` instance that is controlled by this `Navigation`
keypresslock : `LockDraw` to know if the `canvas` key_press_event is
locked
messagelock : `LockDraw` to know if the message is available to write
"""

_default_cursor = cursors.POINTER
_default_tools = [tools.ToolToggleGrid,
tools.ToolToggleFullScreen,
tools.ToolQuit,
tools.ToolEnableAllNavigation,
tools.ToolEnableNavigation,
tools.ToolToggleXScale,
tools.ToolToggleYScale,
tools.ToolHome, tools.ToolBack,
tools.ToolForward,
None,
tools.ToolZoom,
tools.ToolPan,
None,
'ConfigureSubplots',
'SaveFigure']

def __init__(self, canvas, toolbar=None):

def __init__(self, manager):
""".. automethod:: _toolbar_callback"""

self.canvas = canvas
self.toolbar = self._get_toolbar(toolbar, canvas)
self.manager = manager
self.canvas = manager.canvas
self.toolbar = manager.toolbar

self._key_press_handler_id = self.canvas.mpl_connect('key_press_event',
self._key_press)
self._key_press_handler_id = self.canvas.mpl_connect(
'key_press_event', self._key_press)

self._idDrag = self.canvas.mpl_connect('motion_notify_event',
self._mouse_move)
self._idDrag = self.canvas.mpl_connect(
'motion_notify_event', self._mouse_move)

# a dict from axes index to a list of view limits
self.views = cbook.Stack()
Expand All @@ -3238,36 +3224,15 @@ def __init__(self, canvas, toolbar=None):
# to write into toolbar message
self.messagelock = widgets.LockDraw()

for tool in self._default_tools:
for name, tool in tools.tools:
if tool is None:
if self.toolbar is not None:
self.toolbar.add_separator(-1)
else:
self.add_tool(tool)
self.add_tool(name, tool, None)
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't feel like the right place for this for block.

I propose:

  1. Moving this into a separate method NavigationBase.addTools.
  2. Adding the default tools later, as it feels like a separate concept. The vessel and what we choose to fill it with. (This also hinders my other work).

I have applied point 1, looking for the best place to do 2. I shall submit a PR with just this later today.

Copy link
Member

Choose a reason for hiding this comment

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

@fariza Both points applied here (as discussed in workflow, or should I send as a PR to your branch?):
OceanWolf/matplotlib@fariza:navigation-toolbar-coexistence...navigation-toolbar-coexistence-event-framework

I still don't quite like the structure of tools.tools, it seems a bit messy having separators in there, as they do not act as a tool.

Given that you accept this new API method addTools, I propose the following changes for the attribute tools to:

tools: (tool | tool_group, tool | tool_group, ...) where
tool_group: (tool, tool, ...) and
tool: (name, class, in_toolbar)

where a separator gets automatically placed between groups.

Just looked back at your comments in #3232 and see that you proposed something similar wrt add_tool.

Shall I code these changes up?

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 Send the PR to my branch, so I can merge it.

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 I don't get it, why the default tools are not added by default to Navigation?
This means, anybody that is not using pyplot has to add tools by hand...

Copy link
Member

Choose a reason for hiding this comment

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

@fariza PR sent.
Apologies, I have only ever used pyplot, so I had no awareness of other use-cases. I agree, not good.

The reasoning behind that change goes as follows:

  1. Separation between container and contents as a design principle gives greater flexibility to user workflows and allows for example say in the future, to create custom defaults.
  2. Ensures that FigureManager and Navigation both exist before adding tools to them. I ran into trouble with attaching event listeners to the tools, as the tools got set up too early and created a circular dependency, hitting breaking point in ToolBase.set_figure().

If you let me know the various other use-cases/entry points, I shall come up with a different solution :D.

Copy link
Member Author

Choose a reason for hiding this comment

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

If you don't put add_tools in pyplot where would you put it in... for example the gtk3 backend? So it gets called automatically

Copy link
Member

Choose a reason for hiding this comment

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

I would have to look into the various usecases for matplotlib, to look at entry points, but your example of the gtk3 backend could work nicely if we wanted backend customised tools/toolsets added... for example your CopyToolGTK3 in the navigation example.

Copy link
Member

Choose a reason for hiding this comment

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

I just want to chime in (not having followed this thread super closely) that I think it is important to separate the building of the windows (currently done via the manager* functions) and pyplot which at it's core is a dictionary of currently open figures. Currently, these two things are mixed up, but they should not be (this is the point of #2624 which I should revive work on once we get 1.4.0 released (so close)).


self._last_cursor = self._default_cursor

@classmethod
def get_default_tools(cls):
"""Get the default tools"""

return cls._default_tools

@classmethod
def set_default_tools(cls, tools):
"""Set default tools"""

cls._default_tools = tools

def _get_toolbar(self, toolbar, canvas):
# must be inited after the window, drawingArea and figure
# attrs are set
if rcParams['toolbar'] == 'navigation' and toolbar is not None:
toolbar = toolbar(canvas.manager)
else:
toolbar = None
return toolbar

@property
def active_toggle(self):
"""Toggled Tool
Expand Down Expand Up @@ -3339,8 +3304,7 @@ def unregister(self, name):
This method is used by `PersistentTools` to remove the reference kept
by `Navigation`.

It is usually called by the `deactivate` method or during
destroy if it is a graphical Tool.
It is usually called by the `unregister` method

If called, next time the `Tool` is used it will be reinstantiated
instead of using the existing instance.
Expand Down Expand Up @@ -3369,29 +3333,27 @@ def remove_tool(self, name):
if self.toolbar:
self.toolbar._remove_toolitem(name)

def add_tool(self, tool):
def add_tool(self, name, tool, position=None):
"""Add tool to `Navigation`

Parameters
----------
name : string
Name of the tool, treated as the ID, has to be unique
tool : string or `Tool` class
Reference to find the class of the Tool to be added
position : int or None (default)
Position in the toolbar, if None, is positioned at the end
"""

tool_cls = self._get_cls_to_instantiate(tool)
if tool_cls is False:
warnings.warn('Impossible to find class for %s' % str(tool))
return
name = tool_cls.name

if name is None:
warnings.warn('tool_clss need a name to be added, it is used '
'as ID')
return
if name in self._tools:
warnings.warn('A tool_cls with the same name already exist, '
'not added')

return

self._tools[name] = tool_cls
Expand All @@ -3402,7 +3364,7 @@ def add_tool(self, tool):
(k, self._keys[k], name))
self._keys[k] = name

if self.toolbar and tool_cls.position is not None:
if self.toolbar and tool_cls.intoolbar:
# TODO: better search for images, they are not always in the
# datapath
basedir = os.path.join(rcParams['datapath'], 'images')
Expand All @@ -3411,10 +3373,11 @@ def add_tool(self, tool):
else:
fname = None
toggle = issubclass(tool_cls, tools.ToolToggleBase)
self.toolbar._add_toolitem(name, tool_cls.description,
fname,
tool_cls.position,
toggle)
self.toolbar._add_toolitem(name,
tool_cls.description,
fname,
position,
toggle)

def _get_cls_to_instantiate(self, callback_class):
if isinstance(callback_class, six.string_types):
Expand Down Expand Up @@ -3463,7 +3426,7 @@ def _key_press(self, event):

def _get_instance(self, name):
if name not in self._instances:
instance = self._tools[name](self.canvas.figure)
instance = self._tools[name](self.canvas.figure, name)
# register instance
self._instances[name] = instance

Expand Down Expand Up @@ -3509,26 +3472,23 @@ def _handle_toggle(self, name, event=None, from_toolbar=False):
for a in self.canvas.figure.get_axes():
a.set_navigate_mode(self._toggled)

def list_tools(self):
"""Print the list the tools controlled by `Navigation`"""
def get_tools(self):
"""Return the tools controlled by `Navigation`"""

print ('_' * 80)
print ("{0:20} {1:50} {2}".format('Name (id)', 'Tool description',
'Keymap'))
print ('_' * 80)
d = {}
for name in sorted(self._tools.keys()):
tool = self._tools[name]
keys = [k for k, i in six.iteritems(self._keys) if i == name]
print ("{0:20} {1:50} {2}".format(tool.name, tool.description,
', '.join(keys)))
print ('_' * 80, '\n')
d[name] = {'cls': tool,
'description': tool.description,
'keymap': keys}
return d

def update(self):
"""Reset the axes stack"""

self.views.clear()
self.positions.clear()
# self.set_history_buttons()

def _mouse_move(self, event):
if not event.inaxes or not self._toggled:
Expand Down Expand Up @@ -3625,7 +3585,6 @@ def push_current(self):
a.get_position().frozen()))
self.views.push(lims)
self.positions.push(pos)
# self.set_history_buttons()

def draw_rubberband(self, event, caller, x0, y0, x1, y1):
"""Draw a rectangle rubberband to indicate zoom limits
Expand Down Expand Up @@ -3677,7 +3636,7 @@ def __init__(self, manager):
self.manager = manager

def _add_toolitem(self, name, description, image_file, position,
toggle):
toggle):
"""Add a toolitem to the toolbar

The callback associated with the button click event,
Expand Down Expand Up @@ -3734,7 +3693,8 @@ def _toggle(self, name, callback=False):

"""

# carefull, callback means to perform or not the callback while toggling
# carefull, callback means to perform or not the callback while
# toggling
raise NotImplementedError

def _remove_toolitem(self, name):
Expand Down
Loading
0