From 00824666129917301b21404b364879cd4b732495 Mon Sep 17 00:00:00 2001 From: John Hunter Date: Sun, 26 Feb 2012 08:48:45 -0600 Subject: [PATCH 1/3] factored out default mpl key handling so user interface code could access it; illustrate in gtk, tk and qt examples --- examples/user_interfaces/embedding_in_gtk2.py | 8 + .../embedding_in_qt4_wtoolbar.py | 63 ++++++ examples/user_interfaces/embedding_in_tk.py | 10 + lib/matplotlib/backend_bases.py | 202 ++++++++++-------- 4 files changed, 191 insertions(+), 92 deletions(-) create mode 100644 examples/user_interfaces/embedding_in_qt4_wtoolbar.py diff --git a/examples/user_interfaces/embedding_in_gtk2.py b/examples/user_interfaces/embedding_in_gtk2.py index ede7b25c94cc..44b942722214 100644 --- a/examples/user_interfaces/embedding_in_gtk2.py +++ b/examples/user_interfaces/embedding_in_gtk2.py @@ -17,6 +17,8 @@ #from matplotlib.backends.backend_gtk import NavigationToolbar2GTK as NavigationToolbar from matplotlib.backends.backend_gtkagg import NavigationToolbar2GTKAgg as NavigationToolbar +# implement the default mpl key bindings +from matplotlib.backend_bases import key_press_handler win = gtk.Window() win.connect("destroy", lambda x: gtk.main_quit()) @@ -40,5 +42,11 @@ vbox.pack_start(toolbar, False, False) +def on_key_event(event): + print('you pressed %s'%event.key) + key_press_handler(event, canvas, toolbar) + +canvas.mpl_connect('key_press_event', on_key_event) + win.show_all() gtk.main() diff --git a/examples/user_interfaces/embedding_in_qt4_wtoolbar.py b/examples/user_interfaces/embedding_in_qt4_wtoolbar.py new file mode 100644 index 000000000000..f33623254fa3 --- /dev/null +++ b/examples/user_interfaces/embedding_in_qt4_wtoolbar.py @@ -0,0 +1,63 @@ +import sys +import numpy as np +from matplotlib.figure import Figure +from matplotlib.backend_bases import FigureManagerBase, key_press_handler +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar + + +class AppForm(QMainWindow): + def __init__(self, parent=None): + QMainWindow.__init__(self, parent) + #self.x, self.y = self.get_data() + self.data = self.get_data2() + self.create_main_frame() + self.on_draw() + + def create_main_frame(self): + self.main_frame = QWidget() + + self.fig = Figure((5.0, 4.0), dpi=100) + self.canvas = FigureCanvas(self.fig) + self.canvas.setParent(self.main_frame) + self.canvas.setFocusPolicy( Qt.ClickFocus ) + self.canvas.setFocus() + + self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) + + self.canvas.mpl_connect('key_press_event', self.on_key_press) + + vbox = QVBoxLayout() + vbox.addWidget(self.canvas) # the matplotlib canvas + vbox.addWidget(self.mpl_toolbar) + self.main_frame.setLayout(vbox) + self.setCentralWidget(self.main_frame) + + def get_data2(self): + return np.arange(20).reshape([4,5]).copy() + + def on_draw(self): + self.fig.clear() + self.axes = self.fig.add_subplot(111) + #self.axes.plot(self.x, self.y, 'ro') + #self.axes.imshow(self.data, interpolation='nearest') + self.axes.plot([1,2,3]) + self.canvas.draw() + + def on_key_press(self, event): + print 'you pressed', event.key + # implement the default mpl key press events described at + # http://matplotlib.sourceforge.net/users/navigation_toolbar.html#navigation-keyboard-shortcuts + key_press_handler(event, self.canvas, self.mpl_toolbar) + +def main(): + app = QApplication(sys.argv) + form = AppForm() + form.show() + app.exec_() + +if __name__ == "__main__": + main() diff --git a/examples/user_interfaces/embedding_in_tk.py b/examples/user_interfaces/embedding_in_tk.py index 4327a67b4cc7..1868ea68f45b 100755 --- a/examples/user_interfaces/embedding_in_tk.py +++ b/examples/user_interfaces/embedding_in_tk.py @@ -5,6 +5,10 @@ from numpy import arange, sin, pi from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg +# implement the default mpl key bindings +from matplotlib.backend_bases import key_press_handler + + from matplotlib.figure import Figure import sys @@ -34,6 +38,12 @@ toolbar.update() canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) +def on_key_event(event): + print('you pressed %s'%event.key) + key_press_handler(event, canvas, toolbar) + +canvas.mpl_connect('key_press_event', on_key_event) + def _quit(): root.quit() # stops mainloop root.destroy() # this is necessary on Windows to prevent diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index be4ee71ef089..f8515a793707 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -1436,7 +1436,7 @@ def __init__(self, figure): self.button_pick_id = self.mpl_connect('button_press_event',self.pick) self.scroll_pick_id = self.mpl_connect('scroll_event',self.pick) self.mouse_grabber = None # the axes currently grabbing mouse - + self.toolbar = None # NavigationToolbar2 will set me if False: ## highlight the artists that are hit self.mpl_connect('motion_notify_event',self.onHilite) @@ -2213,6 +2213,110 @@ def stop_event_loop_default(self): self._looping = False +def key_press_handler(event, canvas, toolbar=None): + """ + Implement the default mpl key bindings for the canvas and toolbar + described at :ref:`navigation-keyboard-shortcuts` + + *event* + a :class:`KeyEvent` instance + *canvas* + a :class:`FigureCanvasBase` instance + *toolbar* + a class:`NavigationToolbar2` instance + + """ + # these bindings happen whether you are over an axes or not + #if event.key == 'q': + # self.destroy() # how cruel to have to destroy oneself! + # return + + if event.key is None: + return + + # Load key-mappings from your matplotlibrc file. + fullscreen_keys = rcParams['keymap.fullscreen'] + home_keys = rcParams['keymap.home'] + back_keys = rcParams['keymap.back'] + forward_keys = rcParams['keymap.forward'] + pan_keys = rcParams['keymap.pan'] + zoom_keys = rcParams['keymap.zoom'] + save_keys = rcParams['keymap.save'] + grid_keys = rcParams['keymap.grid'] + toggle_yscale_keys = rcParams['keymap.yscale'] + toggle_xscale_keys = rcParams['keymap.xscale'] + all = rcParams['keymap.all_axes'] + + # toggle fullscreen mode (default key 'f') + if event.key in fullscreen_keys: + self.full_screen_toggle() + + if toolbar is not None: + # home or reset mnemonic (default key 'h', 'home' and 'r') + if event.key in home_keys: + toolbar.home() + # forward / backward keys to enable left handed quick navigation + # (default key for backward: 'left', 'backspace' and 'c') + elif event.key in back_keys: + toolbar.back() + # (default key for forward: 'right' and 'v') + elif event.key in forward_keys: + toolbar.forward() + # pan mnemonic (default key 'p') + elif event.key in pan_keys: + toolbar.pan() + # zoom mnemonic (default key 'o') + elif event.key in zoom_keys: + toolbar.zoom() + # saving current figure (default key 's') + elif event.key in save_keys: + toolbar.save_figure() + + if event.inaxes is None: + return + + # the mouse has to be over an axes to trigger these + # switching on/off a grid in current axes (default key 'g') + if event.key in grid_keys: + event.inaxes.grid() + canvas.draw() + # toggle scaling of y-axes between 'log and 'linear' (default key 'l') + elif event.key in toggle_yscale_keys: + ax = event.inaxes + scale = ax.get_yscale() + if scale == 'log': + ax.set_yscale('linear') + ax.figure.canvas.draw() + elif scale == 'linear': + ax.set_yscale('log') + ax.figure.canvas.draw() + # toggle scaling of x-axes between 'log and 'linear' (default key 'k') + elif event.key in toggle_xscale_keys: + ax = event.inaxes + scalex = ax.get_xscale() + if scalex == 'log': + ax.set_xscale('linear') + ax.figure.canvas.draw() + elif scalex == 'linear': + ax.set_xscale('log') + ax.figure.canvas.draw() + + elif (event.key.isdigit() and event.key!='0') or event.key in all: + # keys in list 'all' enables all axes (default key 'a'), + # otherwise if key is a number only enable this particular axes + # if it was the axes, where the event was raised + if not (event.key in all): + n = int(event.key)-1 + for i, a in enumerate(canvas.figure.get_axes()): + # consider axes, in which the event was raised + # FIXME: Why only this axes? + if event.x is not None and event.y is not None \ + and a.in_axes(event): + if event.key in all: + a.set_navigate(True) + else: + a.set_navigate(i==n) + class FigureManagerBase: """ @@ -2244,97 +2348,11 @@ def resize(self, w, h): pass def key_press(self, event): - - # these bindings happen whether you are over an axes or not - #if event.key == 'q': - # self.destroy() # how cruel to have to destroy oneself! - # return - - if event.key is None: - return - - # Load key-mappings from your matplotlibrc file. - fullscreen_keys = rcParams['keymap.fullscreen'] - home_keys = rcParams['keymap.home'] - back_keys = rcParams['keymap.back'] - forward_keys = rcParams['keymap.forward'] - pan_keys = rcParams['keymap.pan'] - zoom_keys = rcParams['keymap.zoom'] - save_keys = rcParams['keymap.save'] - grid_keys = rcParams['keymap.grid'] - toggle_yscale_keys = rcParams['keymap.yscale'] - toggle_xscale_keys = rcParams['keymap.xscale'] - all = rcParams['keymap.all_axes'] - - # toggle fullscreen mode (default key 'f') - if event.key in fullscreen_keys: - self.full_screen_toggle() - - # home or reset mnemonic (default key 'h', 'home' and 'r') - elif event.key in home_keys: - self.canvas.toolbar.home() - # forward / backward keys to enable left handed quick navigation - # (default key for backward: 'left', 'backspace' and 'c') - elif event.key in back_keys: - self.canvas.toolbar.back() - # (default key for forward: 'right' and 'v') - elif event.key in forward_keys: - self.canvas.toolbar.forward() - # pan mnemonic (default key 'p') - elif event.key in pan_keys: - self.canvas.toolbar.pan() - # zoom mnemonic (default key 'o') - elif event.key in zoom_keys: - self.canvas.toolbar.zoom() - # saving current figure (default key 's') - elif event.key in save_keys: - self.canvas.toolbar.save_figure() - - if event.inaxes is None: - return - - # the mouse has to be over an axes to trigger these - # switching on/off a grid in current axes (default key 'g') - if event.key in grid_keys: - event.inaxes.grid() - self.canvas.draw() - # toggle scaling of y-axes between 'log and 'linear' (default key 'l') - elif event.key in toggle_yscale_keys: - ax = event.inaxes - scale = ax.get_yscale() - if scale == 'log': - ax.set_yscale('linear') - ax.figure.canvas.draw() - elif scale == 'linear': - ax.set_yscale('log') - ax.figure.canvas.draw() - # toggle scaling of x-axes between 'log and 'linear' (default key 'k') - elif event.key in toggle_xscale_keys: - ax = event.inaxes - scalex = ax.get_xscale() - if scalex == 'log': - ax.set_xscale('linear') - ax.figure.canvas.draw() - elif scalex == 'linear': - ax.set_xscale('log') - ax.figure.canvas.draw() - - elif (event.key.isdigit() and event.key!='0') or event.key in all: - # keys in list 'all' enables all axes (default key 'a'), - # otherwise if key is a number only enable this particular axes - # if it was the axes, where the event was raised - if not (event.key in all): - n = int(event.key)-1 - for i, a in enumerate(self.canvas.figure.get_axes()): - # consider axes, in which the event was raised - # FIXME: Why only this axes? - if event.x is not None and event.y is not None \ - and a.in_axes(event): - if event.key in all: - a.set_navigate(True) - else: - a.set_navigate(i==n) - + """ + implement the default mpl key bindings defined at + :ref:`navigation-keyboard-shortcuts` + """ + key_press_handler(event, self.canvas, self.canvas.toolbar) def show_popup(self, msg): """ From 5246c04a821d87bba0036a855f88b1b5f0250802 Mon Sep 17 00:00:00 2001 From: John Hunter Date: Sun, 26 Feb 2012 10:32:22 -0600 Subject: [PATCH 2/3] fix some doc problems --- doc/users/navigation_toolbar.rst | 2 ++ lib/matplotlib/backend_bases.py | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/users/navigation_toolbar.rst b/doc/users/navigation_toolbar.rst index 609d130b0766..66848eab4e36 100644 --- a/doc/users/navigation_toolbar.rst +++ b/doc/users/navigation_toolbar.rst @@ -76,6 +76,8 @@ The ``Save`` button ``svg`` and ``pdf``. +.. _key-event-handling: + Navigation Keyboard Shortcuts ----------------------------- diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index f8515a793707..d6592bc5bf86 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2216,14 +2216,14 @@ def stop_event_loop_default(self): def key_press_handler(event, canvas, toolbar=None): """ Implement the default mpl key bindings for the canvas and toolbar - described at :ref:`navigation-keyboard-shortcuts` + described at :ref:`key-event-handling` *event* a :class:`KeyEvent` instance *canvas* a :class:`FigureCanvasBase` instance *toolbar* - a class:`NavigationToolbar2` instance + a :class:`NavigationToolbar2` instance """ # these bindings happen whether you are over an axes or not @@ -2328,7 +2328,7 @@ class FigureManagerBase: A :class:`FigureCanvasBase` instance *num* - The figure nuamber + The figure number """ def __init__(self, canvas, num): self.canvas = canvas @@ -2350,7 +2350,7 @@ def resize(self, w, h): def key_press(self, event): """ implement the default mpl key bindings defined at - :ref:`navigation-keyboard-shortcuts` + :ref:`key-event-handling` """ key_press_handler(event, self.canvas, self.canvas.toolbar) From b3e43e6454a9b13811aa21b2bd5a7f15c93f1c8b Mon Sep 17 00:00:00 2001 From: John Hunter Date: Sun, 26 Feb 2012 10:42:37 -0600 Subject: [PATCH 3/3] use StrongFocus as the default policy and restore imshow from orig example --- examples/user_interfaces/embedding_in_qt4_wtoolbar.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/user_interfaces/embedding_in_qt4_wtoolbar.py b/examples/user_interfaces/embedding_in_qt4_wtoolbar.py index f33623254fa3..37e84a8d177c 100644 --- a/examples/user_interfaces/embedding_in_qt4_wtoolbar.py +++ b/examples/user_interfaces/embedding_in_qt4_wtoolbar.py @@ -23,7 +23,7 @@ def create_main_frame(self): self.fig = Figure((5.0, 4.0), dpi=100) self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.main_frame) - self.canvas.setFocusPolicy( Qt.ClickFocus ) + self.canvas.setFocusPolicy( Qt.StrongFocus ) self.canvas.setFocus() self.mpl_toolbar = NavigationToolbar(self.canvas, self.main_frame) @@ -43,8 +43,8 @@ def on_draw(self): self.fig.clear() self.axes = self.fig.add_subplot(111) #self.axes.plot(self.x, self.y, 'ro') - #self.axes.imshow(self.data, interpolation='nearest') - self.axes.plot([1,2,3]) + self.axes.imshow(self.data, interpolation='nearest') + #self.axes.plot([1,2,3]) self.canvas.draw() def on_key_press(self, event):