diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index d6592bc5bf86..3a8ee46168e3 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1693,18 +1693,27 @@ def leave_notify_event(self, guiEvent=None): the native UI event that generated the mpl event """ + self.callbacks.process('figure_leave_event', LocationEvent.lastevent) LocationEvent.lastevent = None + self._lastx, self._lasty = None, None - def enter_notify_event(self, guiEvent=None): + def enter_notify_event(self, guiEvent=None, xy=None): """ Backend derived classes should call this function when entering canvas *guiEvent* the native UI event that generated the mpl event + *xy* + the coordinate location of the pointer when the canvas is + entered """ + if xy is not None: + x, y = xy + self._lastx, self._lasty = x, y + event = Event('figure_enter_event', self, guiEvent) self.callbacks.process('figure_enter_event', event) diff --git a/lib/matplotlib/backends/backend_gtk.py b/lib/matplotlib/backends/backend_gtk.py index c7c2a8a6b4db..399f3cf592dd 100644 --- a/lib/matplotlib/backends/backend_gtk.py +++ b/lib/matplotlib/backends/backend_gtk.py @@ -312,7 +312,8 @@ def leave_notify_event(self, widget, event): FigureCanvasBase.leave_notify_event(self, event) def enter_notify_event(self, widget, event): - FigureCanvasBase.enter_notify_event(self, event) + x, y, state = event.window.get_pointer() + FigureCanvasBase.enter_notify_event(self, event, xy=(x,y)) def _get_key(self, event): if event.keyval in self.keyvald: diff --git a/lib/matplotlib/backends/backend_qt4.py b/lib/matplotlib/backends/backend_qt4.py index 3d8dbf2a69cc..26225a9ca33e 100644 --- a/lib/matplotlib/backends/backend_qt4.py +++ b/lib/matplotlib/backends/backend_qt4.py @@ -317,8 +317,13 @@ def __init__( self, canvas, num ): image = os.path.join( matplotlib.rcParams['datapath'],'images','matplotlib.png' ) self.window.setWindowIcon(QtGui.QIcon( image )) - # Give the keyboard focus to the figure instead of the manager - self.canvas.setFocusPolicy( QtCore.Qt.ClickFocus ) + # Give the keyboard focus to the figure instead of the + # manager; StrongFocus accepts both tab and click to focus and + # will enable the canvas to process event w/o clicking. + # ClickFocus only takes the focus is the window has been + # clicked + # on. http://developer.qt.nokia.com/doc/qt-4.8/qt.html#FocusPolicy-enum + self.canvas.setFocusPolicy( QtCore.Qt.StrongFocus ) self.canvas.setFocus() QtCore.QObject.connect( self.window, QtCore.SIGNAL( 'destroyed()' ), diff --git a/lib/matplotlib/backends/backend_tkagg.py b/lib/matplotlib/backends/backend_tkagg.py index 0ae85006fee4..f93d7f95de18 100644 --- a/lib/matplotlib/backends/backend_tkagg.py +++ b/lib/matplotlib/backends/backend_tkagg.py @@ -237,6 +237,75 @@ def resize(self, event): self.resize_event() self.show() + # a resizing will in general move the pointer position + # relative to the canvas, so process it as a motion notify + # event. An intended side effect of this call is to allow + # window raises (which trigger a resize) to get the cursor + # position to the mpl event framework so key presses which are + # over the axes will work w/o clicks or explicit motion + self._update_pointer_position(event) + + def _update_pointer_position(self, guiEvent=None): + """ + Figure out if we are inside the canvas or not and update the + canvas enter/leave events + """ + # if the pointer if over the canvas, set the lastx and lasty + # attrs of the canvas so it can process event w/o mouse click + # or move + + # the window's upper, left coords in screen coords + xw = self._tkcanvas.winfo_rootx() + yw = self._tkcanvas.winfo_rooty() + # the pointer's location in screen coords + xp, yp = self._tkcanvas.winfo_pointerxy() + + # not figure out the canvas coordinates of the pointer + xc = xp - xw + yc = yp - yw + + # flip top/bottom + yc = self.figure.bbox.height - yc + + # JDH: this method was written originally to get the pointer + # location to the backend lastx and lasty attrs so that events + # like KeyEvent can be handled without mouse events. Eg, if + # the cursor is already above the axes, then key presses like + # 'g' should toggle the grid. In order for this to work in + # backend_bases, the canvas needs to know _lastx and _lasty. + # There are three ways to get this info the canvas: + # + # 1) set it explicity + # + # 2) call enter/leave events explicity. The downside of this + # in the impl below is that enter could be repeatedly + # triggered if thes mouse is over the axes and one is + # resizing with the keyboard. This is not entirely bad, + # because the mouse position relative to the canvas is + # changing, but it may be surprising to get repeated entries + # without leaves + # + # 3) process it as a motion notify event. This also has pros + # and cons. The mouse is moving relative to the window, but + # this may surpise an event handler writer who is getting + # motion_notify_events even if the mouse has not moved + + # here are the three scenarios + if 1: + # just manually set it + self._lastx, self._lasty = xc, yc + elif 0: + # alternate implementation: process it as a motion + FigureCanvasBase.motion_notify_event(self, xc, yc, guiEvent) + elif 0: + # alternate implementation -- process enter/leave events + # instead of motion/notify + if self.figure.bbox.contains(xc, yc): + self.enter_notify_event(guiEvent, xy=(xc,yc)) + else: + self.leave_notify_event(guiEvent) + + def draw(self): FigureCanvasAgg.draw(self) tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)