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
reducing number of locks
  • Loading branch information
fariza committed Apr 7, 2015
commit f6a2f196dc5ea1cf8517fa97d8ed9fe94c9f390f
91 changes: 34 additions & 57 deletions lib/matplotlib/backend_bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -3232,11 +3232,9 @@ class NavigationBase(object):
----------
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`
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,
Expand Down Expand Up @@ -3266,11 +3264,6 @@ def __init__(self, canvas, toolbar=None):
self._idDrag = self.canvas.mpl_connect('motion_notify_event',
self._mouse_move)

self._idPress = self.canvas.mpl_connect('button_press_event',
self._press)
self._idRelease = self.canvas.mpl_connect('button_release_event',
self._release)

# a dict from axes index to a list of view limits
self.views = cbook.Stack()
self.positions = cbook.Stack() # stack of subplot positions
Expand All @@ -3282,11 +3275,7 @@ def __init__(self, canvas, toolbar=None):

#to communicate with tools and redirect events
self.keypresslock = widgets.LockDraw()
self.movelock = widgets.LockDraw()
self.presslock = widgets.LockDraw()
self.releaselock = widgets.LockDraw()
#just to group all the locks in one place
self.canvaslock = self.canvas.widgetlock
self.messagelock = widgets.LockDraw()

for tool in self._default_tools:
if tool is None:
Expand Down Expand Up @@ -3479,16 +3468,9 @@ def _trigger_tool(self, name, event, from_toolbar):
tool(self.canvas.figure, event)

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

#some tools may need to capture keypress, but they need to be toggle
if self._toggled:
instance = self._get_instance(self._toggled)
if self.keypresslock.isowner(instance):
instance.key_press(event)
return

name = self._keys.get(event.key, None)
self._trigger_tool(name, event, False)

Expand Down Expand Up @@ -3559,12 +3541,6 @@ def update(self):
# self.set_history_buttons()

def _mouse_move(self, event):
if self._toggled:
instance = self._instances[self._toggled]
if self.movelock.isowner(instance):
instance.mouse_move(event)
return

if not event.inaxes or not self._toggled:
if self._last_cursor != self._default_cursor:
self.set_cursor(self._default_cursor)
Expand All @@ -3576,7 +3552,7 @@ def _mouse_move(self, event):
self.set_cursor(cursor)
self._last_cursor = cursor

if self.toolbar is None:
if self.toolbar is None or self.messagelock.locked():
return

if event.inaxes and event.inaxes.get_navigate():
Expand All @@ -3593,30 +3569,6 @@ def _mouse_move(self, event):
else:
self.toolbar.set_message('')

def _release(self, event):
if self._toggled:
instance = self._instances[self._toggled]
if self.releaselock.isowner(instance):
instance.release(event)
return
self.release(event)

def release(self, event):
pass

def _press(self, event):
"""Called whenver a mouse button is pressed."""
if self._toggled:
instance = self._instances[self._toggled]
if self.presslock.isowner(instance):
instance.press(event)
return
self.press(event)

def press(self, event):
"""Called whenver a mouse button is pressed."""
pass

def draw(self):
"""Redraw the canvases, update the locators"""
for a in self.canvas.figure.get_axes():
Expand Down Expand Up @@ -3681,9 +3633,34 @@ def push_current(self):
self.positions.push(pos)
# self.set_history_buttons()

def draw_rubberband(self, event, x0, y0, x1, y1):
"""Draw a rectangle rubberband to indicate zoom limits"""
pass
def draw_rubberband(self, event, caller, x0, y0, x1, y1):
"""Draw a rectangle rubberband to indicate zoom limits

Draw a rectanlge in the canvas, if
`self.canvas.widgetlock` is available to **caller**

Parameters
----------
event : `FigureCanvas` event
caller : instance trying to draw the rubberband
x0, y0, x1, y1 : coordinates
"""
if not self.canvas.widgetlock.available(caller):
warnings.warn("%s doesn't own the canvas widgetlock" % caller)

def remove_rubberband(self, event, caller):
"""Remove the rubberband

Remove the rubberband if the `self.canvas.widgetlock` is
available to **caller**

Parameters
----------
event : `FigureCanvas` event
caller : instance trying to remove the rubberband
"""
if not self.canvas.widgetlock.available(caller):
warnings.warn("%s doesn't own the canvas widgetlock" % caller)


class ToolbarBase(object):
Expand Down
128 changes: 54 additions & 74 deletions lib/matplotlib/backend_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,37 +141,13 @@ def unregister(self, *args):
class ToolToggleBase(ToolPersistentBase):
"""Toggleable tool

This tool is a Persistent Tool, that has the ability to capture
the keypress, press and release events, preventing other tools
to use the same events at the same time
This tool is a Persistent Tool that has a toggled state.
Every time it is triggered, it switches between enable and disable

"""
toggle = True
_toggled = False

def mouse_move(self, event):
"""Mouse move event

Called when a motion_notify_event is emited by the `FigureCanvas` if
`navigation.movelock(self)` was setted
"""
pass

def press(self, event):
"""Mouse press event

Called when a button_press_event is emited by the `FigureCanvas` if
`navigation.presslock(self)` was setted
"""
pass

def release(self, event):
"""Mouse release event

Called when a button_release_event is emited by the `FigureCanvas` if
`navigation.releaselock(self)` was setted
"""
pass

def trigger(self, event):
if self._toggled:
self.disable(event)
Expand All @@ -182,26 +158,23 @@ def trigger(self, event):
def enable(self, event=None):
"""Enable the toggle tool

This method is called when the tool is triggered and not active
This method is called when the tool is triggered and not toggled
"""
pass

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

This method is called when the tool is triggered and active.
* Second click on the toolbar button
This method is called when the tool is triggered and toggled.
* Second click on the toolbar tool button
* Another toogle tool is triggered (from the same `navigation`)
"""
pass

def key_press(self, event):
"""Key press event

Called when a key_press_event is emited by the `FigureCanvas` if
`navigation.keypresslock(self)` was setted
"""
pass
@property
def toggled(self):
"""State of the toggled tool"""
return self._toggled


class ToolQuit(ToolBase):
Expand Down Expand Up @@ -391,26 +364,29 @@ def __init__(self, *args):
self._ids_zoom = []
self._button_pressed = None
self._xypress = None
self._idPress = None
self._idRelease = None

def enable(self, event):
self.navigation.canvaslock(self)
self.navigation.presslock(self)
self.navigation.releaselock(self)
self.figure.canvas.widgetlock(self)
self._idPress = self.figure.canvas.mpl_connect(
'button_press_event', self._press)
self._idRelease = self.figure.canvas.mpl_connect(
'button_release_event', self._release)

def disable(self, event):
self.navigation.canvaslock.release(self)
self.navigation.presslock.release(self)
self.navigation.releaselock.release(self)
self.figure.canvas.widgetlock.release(self)
self.figure.canvas.mpl_disconnect(self._idPress)
self.figure.canvas.mpl_disconnect(self._idRelease)

def press(self, event):
"""the press mouse button in zoom to rect mode callback"""
def _press(self, event):
"""the _press mouse button in zoom to rect mode callback"""
# If we're already in the middle of a zoom, pressing another
# button works to "cancel"
if self._ids_zoom != []:
self.navigation.movelock.release(self)
for zoom_id in self._ids_zoom:
self.figure.canvas.mpl_disconnect(zoom_id)
self.navigation.release(event)
self.navigation.remove_rubberband(event, self)
self.navigation.draw()
self._xypress = None
self._button_pressed = None
Expand Down Expand Up @@ -439,26 +415,25 @@ def press(self, event):
self._xypress.append((x, y, a, i, a.viewLim.frozen(),
a.transData.frozen()))

self.navigation.movelock(self)
id1 = self.figure.canvas.mpl_connect(
'motion_notify_event', self._mouse_move)
id2 = self.figure.canvas.mpl_connect('key_press_event',
self._switch_on_zoom_mode)
id3 = self.figure.canvas.mpl_connect('key_release_event',
self._switch_off_zoom_mode)

self._ids_zoom = id2, id3
self._ids_zoom = id1, id2, id3
self._zoom_mode = event.key

self.navigation.press(event)

def _switch_on_zoom_mode(self, event):
self._zoom_mode = event.key
self.mouse_move(event)
self._mouse_move(event)

def _switch_off_zoom_mode(self, event):
self._zoom_mode = None
self.mouse_move(event)
self._mouse_move(event)
Copy link
Member

Choose a reason for hiding this comment

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

a little confused... why are we doing drag event processing for a mode switch?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is just to change the rubberband without waiting to move the pointer to see the change


def mouse_move(self, event):
def _mouse_move(self, event):
"""the drag callback in zoom mode"""
if self._xypress:
x, y = event.x, event.y
Expand All @@ -476,11 +451,10 @@ def mouse_move(self, event):
x1, y1, x2, y2 = a.bbox.extents
x, lastx = x1, x2

self.navigation.draw_rubberband(event, x, y, lastx, lasty)
self.navigation.draw_rubberband(event, self, x, y, lastx, lasty)

def release(self, event):
def _release(self, event):
"""the release mouse button callback in zoom to rect mode"""
self.navigation.movelock.release(self)
for zoom_id in self._ids_zoom:
self.figure.canvas.mpl_disconnect(zoom_id)
self._ids_zoom = []
Expand All @@ -496,7 +470,7 @@ def release(self, event):
# ignore singular clicks - 5 pixels is a threshold
if abs(x - lastx) < 5 or abs(y - lasty) < 5:
self._xypress = None
self.navigation.release(event)
self.navigation.remove_rubberband(event, self)
self.navigation.draw()
return

Expand Down Expand Up @@ -604,7 +578,7 @@ def release(self, event):
self._zoom_mode = None

self.navigation.push_current()
self.navigation.release(event)
self.navigation.remove_rubberband(event, self)


class ToolPan(ToolToggleBase):
Expand All @@ -620,18 +594,23 @@ def __init__(self, *args):
ToolToggleBase.__init__(self, *args)
self._button_pressed = None
self._xypress = None
self._idPress = None
self._idRelease = None
self._idDrag = None

def enable(self, event):
self.navigation.canvaslock(self)
self.navigation.presslock(self)
self.navigation.releaselock(self)
self.figure.canvas.widgetlock(self)
self._idPress = self.figure.canvas.mpl_connect(
'button_press_event', self._press)
self._idRelease = self.figure.canvas.mpl_connect(
'button_release_event', self._release)

def disable(self, event):
self.navigation.canvaslock.release(self)
self.navigation.presslock.release(self)
self.navigation.releaselock.release(self)
self.figure.canvas.widgetlock.release(self)
self.figure.canvas.mpl_disconnect(self._idPress)
self.figure.canvas.mpl_disconnect(self._idRelease)

def press(self, event):
def _press(self, event):
if event.button == 1:
self._button_pressed = 1
elif event.button == 3:
Expand All @@ -653,14 +632,16 @@ def press(self, event):
a.get_navigate() and a.can_pan()):
a.start_pan(x, y, event.button)
self._xypress.append((a, i))
self.navigation.movelock(self)
self.navigation.press(event)
self.navigation.messagelock(self)
self._idDrag = self.figure.canvas.mpl_connect(
'motion_notify_event', self._mouse_move)

def release(self, event):
def _release(self, event):
if self._button_pressed is None:
return

self.navigation.movelock.release(self)
self.figure.canvas.mpl_disconnect(self._idDrag)
self.navigation.messagelock.release(self)

for a, _ind in self._xypress:
a.end_pan()
Expand All @@ -669,12 +650,11 @@ def release(self, event):
self._xypress = []
self._button_pressed = None
self.navigation.push_current()
self.navigation.release(event)
self.navigation.draw()

def mouse_move(self, event):
def _mouse_move(self, event):
for a, _ind in self._xypress:
#safer to use the recorded button at the press than current button:
#multiple button can get pressed during motion...
#safer to use the recorded button at the _press than current
#button: #multiple button can get pressed during motion...
a.drag_pan(self._button_pressed, event.key, event.x, event.y)
self.navigation.dynamic_update()
Loading