-
-
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 |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
|
|
||
| :mod:`matplotlib.backend_tools` | ||
| ================================ | ||
|
|
||
| .. automodule:: matplotlib.backend_tools | ||
| :members: | ||
| :undoc-members: | ||
| :show-inheritance: |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,6 +25,14 @@ | |
| the 'show' callable is then set to Show.__call__, inherited from | ||
| ShowBase. | ||
|
|
||
| :class:`NavigationBase` | ||
| The base class for the Navigation class that makes the bridge between | ||
| user interaction (key press, toolbar clicks, ..) and the actions in | ||
| response to the user inputs. | ||
|
|
||
| :class:`ToolbarBase` | ||
| The base class for the Toolbar class of each interactive backend. | ||
|
|
||
| """ | ||
|
|
||
| from __future__ import (absolute_import, division, print_function, | ||
|
|
@@ -3218,6 +3226,25 @@ def set_history_buttons(self): | |
|
|
||
|
|
||
| class NavigationBase(object): | ||
| """ Helper class that groups all the user interactions for a FigureManager | ||
|
|
||
| Attributes | ||
| ---------- | ||
| canvas : `FigureCanvas` instance | ||
| toolbar : `Toolbar` instance that is controlled by this `Navigation` | ||
| keypresslock : `LockDraw` to direct the `canvas` key_press_event | ||
| movelock: `LockDraw` to direct the `canvas` motion_notify_event | ||
| presslock: `LockDraw` to direct the `canvas` button_press_event | ||
| releaselock: `LockDraw` to direct the `canvas` button_release_event | ||
| canvaslock: shortcut to `canvas.widgetlock` | ||
|
|
||
| Notes | ||
| --------_ | ||
| The following methos are for implementation pourposes and not for user use | ||
| For these reason they are defined as **_methodname** (private) | ||
|
|
||
| .. automethod:: _toolbar_callback | ||
| """ | ||
| _default_cursor = cursors.POINTER | ||
| _default_tools = [tools.ToolToggleGrid, | ||
| tools.ToolToggleFullScreen, | ||
|
|
@@ -3286,13 +3313,43 @@ def _get_toolbar(self, toolbar, canvas): | |
| return toolbar | ||
|
|
||
| def get_active(self): | ||
| """Get the active tools | ||
|
|
||
| Returns | ||
| ---------- | ||
| A dictionary with the following elements | ||
| * `toggled`: The currently toggled Tool or None | ||
| * `instances`: List of the currently active tool instances | ||
| that are registered with Navigation | ||
|
|
||
| """ | ||
| return {'toggled': self._toggled, 'instances': self._instances.keys()} | ||
|
|
||
| def get_tool_keymap(self, name): | ||
| """Get the keymap associated with a tool | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the Tool | ||
|
|
||
| Returns | ||
| ---------- | ||
| Keymap : list of keys associated with the Tool | ||
| """ | ||
| keys = [k for k, i in self._keys.items() if i == name] | ||
| return keys | ||
|
|
||
| def set_tool_keymap(self, name, *keys): | ||
| """Set the keymap associated with a tool | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the Tool | ||
| keys : keys to associated with the Tool | ||
| """ | ||
|
|
||
| if name not in self._tools: | ||
| raise AttributeError('%s not in Tools' % name) | ||
|
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 really an AttributeError, is it? Would seem more like a KeyError. |
||
|
|
||
|
|
@@ -3308,50 +3365,79 @@ def set_tool_keymap(self, name, *keys): | |
| self._keys[k] = name | ||
|
|
||
| def unregister(self, name): | ||
| """Unregister the tool from the active instances | ||
|
|
||
| Notes | ||
| ----- | ||
| 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. | ||
|
|
||
| If called, next time the `Tool` is used it will be reinstantiated instead | ||
| of using the existing instance. | ||
| """ | ||
| if self._toggled == name: | ||
| self._handle_toggle(name, from_toolbar=False) | ||
| if name in self._instances: | ||
| del self._instances[name] | ||
|
|
||
| def remove_tool(self, name): | ||
| """Remove tool from the `Navigation` | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the Tool | ||
| """ | ||
| self.unregister(name) | ||
| del self._tools[name] | ||
| keys = [k for k, v in self._keys.items() if v == name] | ||
| for k in keys: | ||
| del self._keys[k] | ||
|
|
||
| if self.toolbar: | ||
| self.toolbar.remove_toolitem(name) | ||
| self.toolbar._remove_toolitem(name) | ||
|
|
||
| def add_tool(self, tool): | ||
| """Add tool to `Navigation` | ||
|
|
||
| def add_tool(self, callback_class): | ||
| tool = self._get_cls_to_instantiate(callback_class) | ||
| name = tool.name | ||
| Parameters | ||
| ---------- | ||
| tool : string or `Tool` class | ||
| Reference to find the class of the Tool to be added | ||
| """ | ||
| tool_cls = self._get_cls_to_instantiate(tool) | ||
| name = tool_cls.name | ||
| if name is None: | ||
| warnings.warn('Tools need a name to be added, it is used as ID') | ||
| 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 with the same name already exist, not added') | ||
| 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 | ||
| if tool.keymap is not None: | ||
| for k in validate_stringlist(tool.keymap): | ||
| self._tools[name] = tool_cls | ||
| if tool_cls.keymap is not None: | ||
| for k in validate_stringlist(tool_cls.keymap): | ||
| if k in self._keys: | ||
| warnings.warn('Key %s changed from %s to %s' % | ||
| (k, self._keys[k], name)) | ||
| self._keys[k] = name | ||
|
|
||
| if self.toolbar and tool.position is not None: | ||
| if self.toolbar and tool_cls.position is not None: | ||
| basedir = os.path.join(rcParams['datapath'], 'images') | ||
| if tool.image is not None: | ||
| fname = os.path.join(basedir, tool.image + '.png') | ||
| if tool_cls.image is not None: | ||
| fname = os.path.join(basedir, tool_cls.image + '.png') | ||
| else: | ||
| fname = None | ||
| self.toolbar.add_toolitem(name, tool.description, | ||
| self.toolbar._add_toolitem(name, tool_cls.description, | ||
| fname, | ||
| tool.position, | ||
| tool.toggle) | ||
| tool_cls.position, | ||
| tool_cls.toggle) | ||
|
|
||
| def _get_cls_to_instantiate(self, callback_class): | ||
| if isinstance(callback_class, basestring): | ||
|
|
@@ -3401,7 +3487,18 @@ def _get_instance(self, name): | |
|
|
||
| return self._instances[name] | ||
|
|
||
| def toolbar_callback(self, name): | ||
| def _toolbar_callback(self, name): | ||
| """Callback for the `Toolbar` | ||
|
|
||
| All Toolbar implementations have to call this method to signal that a | ||
| toolitem was clicked on | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the tool that was activated (click) by the user using the | ||
| toolbar | ||
| """ | ||
| tool = self._tools[name] | ||
| if tool.toggle: | ||
| self._handle_toggle(name, from_toolbar=True) | ||
|
|
@@ -3414,7 +3511,7 @@ def toolbar_callback(self, name): | |
| def _handle_toggle(self, name, event=None, from_toolbar=False): | ||
| #toggle toolbar without callback | ||
| if not from_toolbar and self.toolbar: | ||
| self.toolbar.toggle(name, False) | ||
| self.toolbar._toggle(name, False) | ||
|
|
||
| instance = self._get_instance(name) | ||
| if self._toggled is None: | ||
|
|
@@ -3427,7 +3524,7 @@ def _handle_toggle(self, name, event=None, from_toolbar=False): | |
|
|
||
| else: | ||
| if self.toolbar: | ||
| self.toolbar.toggle(self._toggled, False) | ||
| self.toolbar._toggle(self._toggled, False) | ||
|
|
||
| self._get_instance(self._toggled).deactivate(None) | ||
| instance.activate(None) | ||
|
|
@@ -3437,6 +3534,7 @@ def _handle_toggle(self, name, event=None, from_toolbar=False): | |
| a.set_navigate_mode(self._toggled) | ||
|
|
||
| def list_tools(self): | ||
| """Print the list the tools controlled by `Navigation`""" | ||
| print ('_' * 80) | ||
| print ("{0:20} {1:50} {2}".format('Name (id)', 'Tool description', | ||
| 'Keymap')) | ||
|
|
@@ -3583,29 +3681,113 @@ def draw_rubberband(self, event, x0, y0, x1, y1): | |
|
|
||
|
|
||
| class ToolbarBase(object): | ||
| """Base class for `Toolbar` implementation | ||
|
|
||
| Attributes | ||
| ---------- | ||
| manager : `FigureManager` instance that integrates this `Toolbar` | ||
|
|
||
| Notes | ||
| ----- | ||
| The following methos are for implementation pourposes and not for user use. | ||
| For these reason they are defined as **_methodname** (private) | ||
|
|
||
| .. automethod:: _toggle | ||
| .. automethod:: _add_toolitem | ||
| .. automethod:: _remove_toolitem | ||
| """ | ||
| def __init__(self, manager): | ||
| self.manager = manager | ||
|
|
||
| def add_toolitem(self, name, description, image_file, position, | ||
| def _add_toolitem(self, name, description, image_file, position, | ||
| toggle): | ||
| """Add a toolitem to the toolbar | ||
|
|
||
| The callback associated with the button click event, | ||
| must be **EXACTLY** `self.manager.navigation._toolbar_callback(name)` | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the tool to add, this is used as ID and as default label | ||
| of the buttons | ||
| description : string | ||
| Description of the tool, used for the tooltips | ||
| image_file : string | ||
| Filename of the image for the button or `None` | ||
| position : integer | ||
| Position of the toolitem within the other toolitems | ||
| if -1 at the End | ||
| toggle : bool | ||
| * `True` : The button is a toggle (change the pressed/unpressed | ||
| state between consecutive clicks) | ||
| * `False` : The button is a normal button (returns to unpressed | ||
| state after release) | ||
| """ | ||
| raise NotImplementedError | ||
|
|
||
| def add_separator(self, pos): | ||
| """Add a separator | ||
|
|
||
| Parameters | ||
| ---------- | ||
| pos : integer | ||
| Position where to add the separator within the toolitems | ||
| if -1 at the end | ||
| """ | ||
| pass | ||
|
|
||
| def set_message(self, s): | ||
| """Display a message on toolbar or in status bar""" | ||
| pass | ||
|
|
||
| def toggle(self, name, callback=False): | ||
| def _toggle(self, name, callback=False): | ||
| """Toogle a button | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the button to toggle | ||
| callback : bool | ||
| * `True`: call the button callback during toggle | ||
| * `False`: toggle the button without calling the callback | ||
|
|
||
| """ | ||
| #carefull, callback means to perform or not the callback while toggling | ||
| raise NotImplementedError | ||
|
|
||
| def remove_toolitem(self, name): | ||
| pass | ||
| def _remove_toolitem(self, name): | ||
| """Remove a toolitem from the `Toolbar` | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the tool to remove | ||
|
|
||
| """ | ||
| raise NotImplementedError | ||
|
|
||
| def move_toolitem(self, pos_ini, pos_fin): | ||
| """Change the position of a toolitem | ||
|
|
||
| Parameters | ||
| ---------- | ||
| pos_ini : integer | ||
| Initial position of the toolitem to move | ||
| pos_fin : integer | ||
| Final position of the toolitem | ||
| """ | ||
| pass | ||
|
|
||
| def set_toolitem_visibility(self, name, visible): | ||
| """Change the visibility of a toolitem | ||
|
|
||
| Parameters | ||
| ---------- | ||
| name : string | ||
| Name of the `Tool` | ||
| visible : bool | ||
| * `True`: set the toolitem visible | ||
| * `False`: set the toolitem invisible | ||
| """ | ||
| pass | ||
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.
This isn't lined up with the underscores below
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.
done