diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py
index 9c9249da8fb8..a884cc838860 100644
--- a/lib/matplotlib/backend_tools.py
+++ b/lib/matplotlib/backend_tools.py
@@ -11,6 +11,7 @@
`matplotlib.backend_managers.ToolManager`
"""
+import re
import time
import warnings
from weakref import WeakKeyDictionary
@@ -403,7 +404,7 @@ def trigger(self, sender, event, data=None):
class ToolEnableAllNavigation(ToolBase):
"""Tool to enable all axes for toolmanager interaction"""
- description = 'Enables all axes toolmanager'
+ description = 'Enable all axes toolmanager'
default_keymap = rcParams['keymap.all_axes']
def trigger(self, sender, event, data=None):
@@ -419,7 +420,7 @@ def trigger(self, sender, event, data=None):
class ToolEnableNavigation(ToolBase):
"""Tool to enable a specific axes for toolmanager interaction"""
- description = 'Enables one axes toolmanager'
+ description = 'Enable one axes toolmanager'
default_keymap = (1, 2, 3, 4, 5, 6, 7, 8, 9)
def trigger(self, sender, event, data=None):
@@ -470,7 +471,7 @@ def _get_uniform_grid_state(ticks):
class ToolGrid(_ToolGridBase):
"""Tool to toggle the major grids of the figure"""
- description = 'Toogle major grids'
+ description = 'Toggle major grids'
default_keymap = rcParams['keymap.grid']
def _get_next_grid_states(self, ax):
@@ -491,7 +492,7 @@ def _get_next_grid_states(self, ax):
class ToolMinorGrid(_ToolGridBase):
"""Tool to toggle the major and minor grids of the figure"""
- description = 'Toogle major and minor grids'
+ description = 'Toggle major and minor grids'
default_keymap = rcParams['keymap.grid_minor']
def _get_next_grid_states(self, ax):
@@ -511,7 +512,7 @@ def _get_next_grid_states(self, ax):
class ToolFullScreen(ToolToggleBase):
"""Tool to toggle full screen"""
- description = 'Toogle Fullscreen mode'
+ description = 'Toggle fullscreen mode'
default_keymap = rcParams['keymap.fullscreen']
def enable(self, event):
@@ -541,7 +542,7 @@ def disable(self, event):
class ToolYScale(AxisScaleBase):
"""Tool to toggle between linear and logarithmic scales on the Y axis"""
- description = 'Toogle Scale Y axis'
+ description = 'Toggle scale Y axis'
default_keymap = rcParams['keymap.yscale']
def set_scale(self, ax, scale):
@@ -551,7 +552,7 @@ def set_scale(self, ax, scale):
class ToolXScale(AxisScaleBase):
"""Tool to toggle between linear and logarithmic scales on the X axis"""
- description = 'Toogle Scale X axis'
+ description = 'Toggle scale X axis'
default_keymap = rcParams['keymap.xscale']
def set_scale(self, ax, scale):
@@ -1020,6 +1021,48 @@ def _mouse_move(self, event):
self.toolmanager.canvas.draw_idle()
+class ToolHelpBase(ToolBase):
+ description = 'Print tool list, shortcuts and description'
+ default_keymap = rcParams['keymap.help']
+ image = 'help.png'
+
+ @staticmethod
+ def format_shortcut(key_sequence):
+ """
+ Converts a shortcut string from the notation used in rc config to the
+ standard notation for displaying shortcuts, e.g. 'ctrl+a' -> 'Ctrl+A'.
+ """
+ return (key_sequence if len(key_sequence) == 1 else
+ re.sub(r"\+[A-Z]", r"+Shift\g<0>", key_sequence).title())
+
+ def _format_tool_keymap(self, name):
+ keymaps = self.toolmanager.get_tool_keymap(name)
+ return ", ".join(self.format_shortcut(keymap) for keymap in keymaps)
+
+ def _get_help_text(self):
+ entries = []
+ for name, tool in sorted(self.toolmanager.tools.items()):
+ if not tool.description:
+ continue
+ entries.append(
+ "{}: {}\n\t{}".format(
+ name, self._format_tool_keymap(name), tool.description))
+ return "\n".join(entries)
+
+ def _get_help_html(self):
+ fmt = "
{} | {} | {} |
"
+ rows = [fmt.format(
+ "Action", "Shortcuts", "Description")]
+ for name, tool in sorted(self.toolmanager.tools.items()):
+ if not tool.description:
+ continue
+ rows.append(fmt.format(
+ name, self._format_tool_keymap(name), tool.description))
+ return (""
+ "" + rows[0] + ""
+ "".join(rows[1:]) + "
")
+
+
default_tools = {'home': ToolHome, 'back': ToolBack, 'forward': ToolForward,
'zoom': ToolZoom, 'pan': ToolPan,
'subplots': 'ToolConfigureSubplots',
@@ -1037,12 +1080,13 @@ def _mouse_move(self, event):
_views_positions: ToolViewsPositions,
'cursor': 'ToolSetCursor',
'rubberband': 'ToolRubberband',
+ 'help': 'ToolHelp',
}
"""Default tools"""
default_toolbar_tools = [['navigation', ['home', 'back', 'forward']],
['zoompan', ['pan', 'zoom', 'subplots']],
- ['io', ['save']]]
+ ['io', ['save', 'help']]]
"""Default tools in the toolbar"""
diff --git a/lib/matplotlib/backends/_backend_tk.py b/lib/matplotlib/backends/_backend_tk.py
index fe0d393aa82b..164077cdc713 100644
--- a/lib/matplotlib/backends/_backend_tk.py
+++ b/lib/matplotlib/backends/_backend_tk.py
@@ -1,10 +1,11 @@
import six
-from six.moves import tkinter as Tk
import math
import logging
import os.path
import sys
+import tkinter as Tk
+from tkinter.simpledialog import SimpleDialog
import numpy as np
@@ -963,10 +964,18 @@ def destroy(self, *args, **kwargs):
self.window = None
+class HelpTk(backend_tools.ToolHelpBase):
+ def trigger(self, *args):
+ dialog = SimpleDialog(
+ self.figure.canvas._tkcanvas, self._get_help_text(), ["OK"])
+ dialog.done = lambda num: dialog.frame.master.withdraw()
+
+
backend_tools.ToolSaveFigure = SaveFigureTk
backend_tools.ToolConfigureSubplots = ConfigureSubplotsTk
backend_tools.ToolSetCursor = SetCursorTk
backend_tools.ToolRubberband = RubberbandTk
+backend_tools.ToolHelp = HelpTk
Toolbar = ToolbarTk
diff --git a/lib/matplotlib/backends/backend_gtk3.py b/lib/matplotlib/backends/backend_gtk3.py
index a73cca9ff1b9..5ed0a8db2d3d 100644
--- a/lib/matplotlib/backends/backend_gtk3.py
+++ b/lib/matplotlib/backends/backend_gtk3.py
@@ -846,6 +846,16 @@ def trigger(self, sender, event, data=None):
self.window.present()
+class HelpGTK3(backend_tools.ToolHelpBase):
+ def trigger(self, *args):
+ dialog = Gtk.MessageDialog(
+ self._figure.canvas.get_toplevel(),
+ 0, Gtk.MessageType.INFO, Gtk.ButtonsType.OK, self._get_help_text(),
+ title="Help")
+ dialog.run()
+ dialog.destroy()
+
+
# Define the file to use as the GTk icon
if sys.platform == 'win32':
icon_filename = 'matplotlib.png'
@@ -877,6 +887,7 @@ def error_msg_gtk(msg, parent=None):
backend_tools.ToolConfigureSubplots = ConfigureSubplotsGTK3
backend_tools.ToolSetCursor = SetCursorGTK3
backend_tools.ToolRubberband = RubberbandGTK3
+backend_tools.ToolHelp = HelpGTK3
Toolbar = ToolbarGTK3
diff --git a/lib/matplotlib/backends/backend_qt5.py b/lib/matplotlib/backends/backend_qt5.py
index 0a0bf22e0291..5b94fd19d84b 100644
--- a/lib/matplotlib/backends/backend_qt5.py
+++ b/lib/matplotlib/backends/backend_qt5.py
@@ -1064,10 +1064,16 @@ def remove_rubberband(self):
self.canvas.drawRectangle(None)
+class HelpQt(backend_tools.ToolHelpBase):
+ def trigger(self, *args):
+ QtWidgets.QMessageBox.information(None, "Help", self._get_help_html())
+
+
backend_tools.ToolSaveFigure = SaveFigureQt
backend_tools.ToolConfigureSubplots = ConfigureSubplotsQt
backend_tools.ToolSetCursor = SetCursorQt
backend_tools.ToolRubberband = RubberbandQt
+backend_tools.ToolHelp = HelpQt
def error_msg_qt(msg, parent=None):
diff --git a/lib/matplotlib/mpl-data/images/help.pdf b/lib/matplotlib/mpl-data/images/help.pdf
new file mode 100644
index 000000000000..38178d05b272
Binary files /dev/null and b/lib/matplotlib/mpl-data/images/help.pdf differ
diff --git a/lib/matplotlib/mpl-data/images/help.png b/lib/matplotlib/mpl-data/images/help.png
new file mode 100644
index 000000000000..a52fbbe819e2
Binary files /dev/null and b/lib/matplotlib/mpl-data/images/help.png differ
diff --git a/lib/matplotlib/mpl-data/images/help.ppm b/lib/matplotlib/mpl-data/images/help.ppm
new file mode 100644
index 000000000000..aed6f506df4d
Binary files /dev/null and b/lib/matplotlib/mpl-data/images/help.ppm differ
diff --git a/lib/matplotlib/mpl-data/images/help.svg b/lib/matplotlib/mpl-data/images/help.svg
new file mode 100644
index 000000000000..484bdbcbf659
--- /dev/null
+++ b/lib/matplotlib/mpl-data/images/help.svg
@@ -0,0 +1,52 @@
+
+
+
+
diff --git a/lib/matplotlib/mpl-data/images/help_large.png b/lib/matplotlib/mpl-data/images/help_large.png
new file mode 100644
index 000000000000..3f3d4dfed7e9
Binary files /dev/null and b/lib/matplotlib/mpl-data/images/help_large.png differ
diff --git a/lib/matplotlib/mpl-data/images/help_large.ppm b/lib/matplotlib/mpl-data/images/help_large.ppm
new file mode 100644
index 000000000000..4cf30807b0a1
Binary files /dev/null and b/lib/matplotlib/mpl-data/images/help_large.ppm differ
diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py
index 838af4d974ac..dcd2e6853b86 100644
--- a/lib/matplotlib/rcsetup.py
+++ b/lib/matplotlib/rcsetup.py
@@ -1420,6 +1420,7 @@ def _validate_linestyle(ls):
'keymap.yscale': [['l'], validate_stringlist],
'keymap.xscale': [['k', 'L'], validate_stringlist],
'keymap.all_axes': [['a'], validate_stringlist],
+ 'keymap.help': [['f1'], validate_stringlist],
# sample data
'examples.directory': ['', validate_string],
diff --git a/lib/matplotlib/tests/test_backend_tools.py b/lib/matplotlib/tests/test_backend_tools.py
new file mode 100644
index 000000000000..cc05a1a98f78
--- /dev/null
+++ b/lib/matplotlib/tests/test_backend_tools.py
@@ -0,0 +1,20 @@
+import pytest
+
+from matplotlib.backend_tools import ToolHelpBase
+
+
+@pytest.mark.parametrize('rc_shortcut,expected', [
+ ('home', 'Home'),
+ ('backspace', 'Backspace'),
+ ('f1', 'F1'),
+ ('ctrl+a', 'Ctrl+A'),
+ ('ctrl+A', 'Ctrl+Shift+A'),
+ ('a', 'a'),
+ ('A', 'A'),
+ ('ctrl+shift+f1', 'Ctrl+Shift+F1'),
+ ('1', '1'),
+ ('cmd+p', 'Cmd+P'),
+ ('cmd+1', 'Cmd+1'),
+])
+def test_format_shortcut(rc_shortcut, expected):
+ assert ToolHelpBase.format_shortcut(rc_shortcut) == expected
diff --git a/matplotlibrc.template b/matplotlibrc.template
index cf4e0803106b..a3416144f0f0 100644
--- a/matplotlibrc.template
+++ b/matplotlibrc.template
@@ -583,6 +583,7 @@ backend : $TEMPLATE_BACKEND
#keymap.pan : p ## pan mnemonic
#keymap.zoom : o ## zoom mnemonic
#keymap.save : s, ctrl+s ## saving current figure
+#keymap.help : f1 ## display help about active tools
#keymap.quit : ctrl+w, cmd+w, q ## close the current figure
#keymap.quit_all : W, cmd+W, Q ## close all figures
#keymap.grid : g ## switching on/off major grids in current axes
diff --git a/tools/make_icons.py b/tools/make_icons.py
index 3c9712fa4038..53bb1f023bb3 100755
--- a/tools/make_icons.py
+++ b/tools/make_icons.py
@@ -97,7 +97,8 @@ def make_matplotlib_icon():
('move', 0xf047),
('filesave', 0xf0c7),
('subplots', 0xf1de),
- ('qt4_editor_options', 0xf201)]
+ ('qt4_editor_options', 0xf201),
+ ('help', 0xf128)]
def make_icons():