Skip to content

Help tool. #11045

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 3 commits into from
Apr 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
60 changes: 52 additions & 8 deletions lib/matplotlib/backend_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
`matplotlib.backend_managers.ToolManager`
"""

import re
import time
import warnings
from weakref import WeakKeyDictionary
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -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):
Expand All @@ -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):
Expand Down Expand Up @@ -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())
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't add Shift for single letter shortcuts like 'L' for xscale; is that on purpose?

Copy link
Member

@timhoffm timhoffm Apr 18, 2018

Choose a reason for hiding this comment

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

Good question. I've been implicitly assuming that we keep the case sensitivity for single letters, i.e.

xscale k,L
yscale l

Now thinking about it, this is probably what we want to do. Otherwise, we'd have

xscale K, Shift+L
yscale L

This could be slightly misleading, because "L" really means only typing the L-Key, which in a text field would result in a lowercase "l". An alternative way out (similar to what the GtkShortcutWindow is doing) would be to enclose the keys.

xscale <K>, <Shift>+<L>
yscale <L>

Im +0.3 for sticking with the present solution.
Note: Shortcuts equal to single upper/lowercase letters are rarely used, because they clash with text input. I'm not aware of a standard notation for these cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

vim just uses lowercase for lowercase and uppercase for uppercase (http://vimdoc.sourceforge.net/htmldoc/editing.html#CTRL-^), which seems just fine to me. ctrl+x are always documented as CTRL-X (UPPERCASE), but there are no shortcuts of the form ctrl+shift+x due to internal constraints.


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 = "<tr><td>{}</td><td>{}</td><td>{}</td></tr>"
rows = [fmt.format(
"<b>Action</b>", "<b>Shortcuts</b>", "<b>Description</b>")]
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 ("<style>td {padding: 0px 4px}</style>"
"<table><thead>" + rows[0] + "</thead>"
"<tbody>".join(rows[1:]) + "</tbody></table>")


default_tools = {'home': ToolHome, 'back': ToolBack, 'forward': ToolForward,
'zoom': ToolZoom, 'pan': ToolPan,
'subplots': 'ToolConfigureSubplots',
Expand All @@ -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"""


Expand Down
11 changes: 10 additions & 1 deletion lib/matplotlib/backends/_backend_tk.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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


Expand Down
11 changes: 11 additions & 0 deletions lib/matplotlib/backends/backend_gtk3.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions lib/matplotlib/backends/backend_qt5.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Binary file added lib/matplotlib/mpl-data/images/help.pdf
Binary file not shown.
Binary file added lib/matplotlib/mpl-data/images/help.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added lib/matplotlib/mpl-data/images/help.ppm
Binary file not shown.
52 changes: 52 additions & 0 deletions lib/matplotlib/mpl-data/images/help.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added lib/matplotlib/mpl-data/images/help_large.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added lib/matplotlib/mpl-data/images/help_large.ppm
Binary file not shown.
1 change: 1 addition & 0 deletions lib/matplotlib/rcsetup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
20 changes: 20 additions & 0 deletions lib/matplotlib/tests/test_backend_tools.py
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions matplotlibrc.template
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion tools/make_icons.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down