-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Changes from 1 commit
8cceed4
3118a5a
b4d5fcf
1e8af47
622cb95
d1a9de4
3f89d52
4f3c10b
6065daa
f6a2f19
05db3b6
c08fe56
b207a72
9266447
a53419a
704c717
5056729
e6a4e1e
8942c47
022de6f
2c9a195
cafe668
224f745
94c711e
67257e7
ffa65d6
6739ee0
d18206f
34a52c8
c2da483
44a9b0e
a2ed47f
0665890
411e6e2
d484ebd
75bf97b
6cc040b
0ff5997
af6734f
78513d2
377ff54
7dbbf58
dd66b57
67a414f
e415d8d
1213086
ba61dec
9f2ee2b
9da2b13
110253f
e2804ea
9a64b7e
64f947f
e8cd5d5
4bbcf4e
73a2661
1b83628
e4edd23
d4ac2fb
a7640ef
48a6971
8dafe09
a0695d0
328b169
aac4744
f09b9ef
def3a52
9ee7e25
5eae4e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,33 +1,40 @@ | ||
| import matplotlib | ||
| # matplotlib.use('GTK3Cairo') | ||
| matplotlib.use('TkAGG') | ||
| matplotlib.use('GTK3Cairo') | ||
| # matplotlib.use('TkAGG') | ||
| matplotlib.rcParams['toolbar'] = 'navigation' | ||
| import matplotlib.pyplot as plt | ||
| from matplotlib.backend_tools import ToolBase | ||
|
|
||
|
|
||
| #Create a simple tool to list all the tools | ||
| # Create a simple tool to list all the tools | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer if these comments be class docstrings docstrings instead |
||
| class ListTools(ToolBase): | ||
| #keyboard shortcut | ||
| # 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() | ||
|
|
||
|
|
||
| #A simple example of copy canvas | ||
| #ref: at https://github.com/matplotlib/matplotlib/issues/1987 | ||
| class CopyTool(ToolBase): | ||
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here, should be a class docstring |
||
| class CopyToolGTK3(ToolBase): | ||
| keymap = 'ctrl+c' | ||
| name = 'Copy' | ||
| description = 'Copy canvas' | ||
| position = -1 | ||
| # It is not added to the toolbar as a button | ||
| intoolbar = False | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this need to get removed now?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done, thanks |
||
|
|
||
| def trigger(self, event): | ||
| from gi.repository import Gtk, Gdk | ||
|
|
@@ -41,13 +48,12 @@ def trigger(self, event): | |
| fig = plt.figure() | ||
| plt.plot([1, 2, 3]) | ||
|
|
||
| #If we are in the old toolbar, don't try to modify it | ||
| if matplotlib.rcParams['toolbar'] in ('navigation', 'None'): | ||
| ##Add the custom tools that we created | ||
| fig.canvas.manager.navigation.add_tool(ListTools) | ||
| fig.canvas.manager.navigation.add_tool(CopyTool) | ||
| # Add the custom tools that we created | ||
| fig.canvas.manager.navigation.add_tool('List', ListTools) | ||
| if matplotlib.rcParams['backend'] == 'GTK3Cairo': | ||
| fig.canvas.manager.navigation.add_tool('copy', CopyToolGTK3) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be fine with making this a GTK3Cairo-only example. Makes it a little bit simpler |
||
|
|
||
| ##Just for fun, lets remove the back button | ||
| fig.canvas.manager.navigation.remove_tool('Back') | ||
| # Just for fun, lets remove the back button | ||
| fig.canvas.manager.navigation.remove_tool('Back') | ||
|
|
||
| plt.show() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3230,41 +3230,27 @@ class NavigationBase(object): | |
|
|
||
| Attributes | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't lined up with the underscores below
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
| ---------- | ||
| 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() | ||
|
|
@@ -3280,36 +3266,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) | ||
|
|
||
| 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 | ||
|
|
@@ -3381,8 +3346,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. | ||
|
|
@@ -3411,29 +3375,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: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably should do a
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I don't see the pep8 error here.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be Wouldn't be simpler to just have I suspect @WeatherGod also has a pyflake/pylint turned on.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also, I am reading and commenting top-down. Sorry if some of these seem dumb in light of code I have not read yet.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done @tacaswell change, but still not see the pep8 error
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Different versions of pep8? I find myself curious to learn about this as Travis doesn't complain. |
||
| warnings.warn('Impossible to find class for %s' % str(tool)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this raise an exception? The function failed to achieve its goal and no new tool was added. |
||
| 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: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be done first in this function? Do we want to check to make sure that they are the same type? Again, why warn rather than raise?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used warn because i don't think that adding twice the same tool represents a major fault and your program will keep working as expected (you already have the tool) |
||
| warnings.warn('A tool_cls with the same name already exist, ' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a user won't know what Also, "exist" --> "exists". |
||
| 'not added') | ||
|
|
||
| return | ||
|
|
||
| self._tools[name] = tool_cls | ||
|
|
@@ -3444,7 +3406,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') | ||
|
|
@@ -3453,10 +3415,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): | ||
|
|
@@ -3505,7 +3468,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 | ||
|
|
||
|
|
@@ -3551,26 +3514,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: | ||
|
|
@@ -3667,7 +3627,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 | ||
|
|
@@ -3719,7 +3678,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, | ||
|
|
@@ -3776,7 +3735,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): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these are examples, I would suggest getting rid of extraneous commented out code, and also highlight important lines of code such as this one. For example, is it important that it gets called before importing pyplot? What is it for? Perhaps a short docstring at the top of this example would help explain its purpose/goal that it is demonstrating?