From e8de04c4a2385e088ab421cc0f1fc74acaea7285 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sun, 31 Jul 2016 20:52:13 -0700 Subject: [PATCH 1/4] Add keymap (default: G) to toggle minor grid. If either minor grid is on, turn both of them off. Otherwise, turn all major and minor grids on (it usually doesn't make sense to have the minor grids only). Also change the behavior of `keymap.grid` ("g") to synchronize the grid state of both axes. Otherwise, if starting from only grids in one direction (`plt.plot(); plt.grid(axis="x")`), "g" switches between only `x` grid and only `y` grid, which is unlikely to be the expected behavior. --- doc/users/navigation_toolbar.rst | 3 ++- lib/matplotlib/backend_bases.py | 21 ++++++++++++++++++--- lib/matplotlib/rcsetup.py | 1 + matplotlibrc.template | 1 + 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/doc/users/navigation_toolbar.rst b/doc/users/navigation_toolbar.rst index caa2387e0089..43bacbf02f8c 100644 --- a/doc/users/navigation_toolbar.rst +++ b/doc/users/navigation_toolbar.rst @@ -99,7 +99,8 @@ Close all plots **shift** + **w** Constrain pan/zoom to x axis hold **x** when panning/zooming with mouse Constrain pan/zoom to y axis hold **y** when panning/zooming with mouse Preserve aspect ratio hold **CONTROL** when panning/zooming with mouse -Toggle grid **g** when mouse is over an axes +Toggle major grid **g** when mouse is over an axes +Toggle major and minor grid **G** when mouse is over an axes Toggle x axis scale (log/linear) **L** or **k** when mouse is over an axes Toggle y axis scale (log/linear) **l** when mouse is over an axes ================================== ================================================= diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index d5335dfed6f0..292a278a5e08 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2482,6 +2482,7 @@ def key_press_handler(event, canvas, toolbar=None): save_keys = rcParams['keymap.save'] quit_keys = rcParams['keymap.quit'] grid_keys = rcParams['keymap.grid'] + grid_minor_keys = rcParams['keymap.grid_minor'] toggle_yscale_keys = rcParams['keymap.yscale'] toggle_xscale_keys = rcParams['keymap.xscale'] all = rcParams['keymap.all_axes'] @@ -2525,13 +2526,28 @@ def key_press_handler(event, canvas, toolbar=None): # these bindings require the mouse to be over an axes to trigger + ax = event.inaxes # switching on/off a grid in current axes (default key 'g') if event.key in grid_keys: - event.inaxes.grid() + # If either major grid is on, turn all major and minor grids off. + if any(tick.gridOn + for tick in ax.xaxis.majorTicks + ax.yaxis.majorTicks): + ax.grid(False, which="both") + # Otherwise, turn the major grids on. + else: + ax.grid(True) + canvas.draw() + if event.key in grid_minor_keys: + # If either minor grid is on, turn all minor grids off. + if any(tick.gridOn + for tick in ax.xaxis.minorTicks + ax.yaxis.minorTicks): + ax.grid(False, which="minor") + # Otherwise, turn all major and minor grids on. + else: + ax.grid(True, which="both") 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') @@ -2541,7 +2557,6 @@ def key_press_handler(event, canvas, toolbar=None): 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') diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index ec65a13bc273..e02b85e78e3b 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -1313,6 +1313,7 @@ def validate_animation_writer_path(p): 'keymap.quit': [['ctrl+w', 'cmd+w', 'q'], validate_stringlist], 'keymap.quit_all': [['W', 'cmd+W', 'Q'], validate_stringlist], 'keymap.grid': [['g'], validate_stringlist], + 'keymap.grid_minor': [['G'], validate_stringlist], 'keymap.yscale': [['l'], validate_stringlist], 'keymap.xscale': [['k', 'L'], validate_stringlist], 'keymap.all_axes': [['a'], validate_stringlist], diff --git a/matplotlibrc.template b/matplotlibrc.template index 3889f75f7c5a..91566d6bdbf1 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -585,6 +585,7 @@ backend : $TEMPLATE_BACKEND #keymap.save : s # saving current figure #keymap.quit : ctrl+w, cmd+w # close the current figure #keymap.grid : g # switching on/off a grid in current axes +#keymap.grid_minor : G # switching on/off a grid in current axes #keymap.yscale : l # toggle scaling of y-axes ('log'/'linear') #keymap.xscale : L, k # toggle scaling of x-axes ('log'/'linear') #keymap.all_axes : a # enable all axes From 0c3937c560ea706fec1d0e5ea9b42a12498263e4 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Mon, 1 Aug 2016 20:14:14 -0700 Subject: [PATCH 2/4] Cycle the grid state (none->x->xy->y) for 'g'/'G'. --- .../api_changes/2016-08-02-toggle-grids.rst | 9 +++ lib/matplotlib/backend_bases.py | 69 +++++++++++++------ 2 files changed, 56 insertions(+), 22 deletions(-) create mode 100644 doc/api/api_changes/2016-08-02-toggle-grids.rst diff --git a/doc/api/api_changes/2016-08-02-toggle-grids.rst b/doc/api/api_changes/2016-08-02-toggle-grids.rst new file mode 100644 index 000000000000..fb70385afd0d --- /dev/null +++ b/doc/api/api_changes/2016-08-02-toggle-grids.rst @@ -0,0 +1,9 @@ +Improved toggling of the axes grids +----------------------------------- +The `g` key binding now switches the states of the `x` and `y` grids +independently (by cycling through all four on/off combinations). + +The new `G` key binding switches the states of the minor grids. + +Both bindings are disabled if only a subset of the grid lines (in either +direction) is visible, to avoid making irreversible changes to the figure. diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 292a278a5e08..41d908367968 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2485,7 +2485,7 @@ def key_press_handler(event, canvas, toolbar=None): grid_minor_keys = rcParams['keymap.grid_minor'] toggle_yscale_keys = rcParams['keymap.yscale'] toggle_xscale_keys = rcParams['keymap.xscale'] - all = rcParams['keymap.all_axes'] + all_keys = rcParams['keymap.all_axes'] # toggle fullscreen mode (default key 'f') if event.key in fullscreen_keys: @@ -2525,47 +2525,72 @@ def key_press_handler(event, canvas, toolbar=None): return # these bindings require the mouse to be over an axes to trigger + def _get_uniform_gridstate(ticks): + # Return True/False if all grid lines are on or off, None if they are + # not all in the same state. + if all(tick.gridOn for tick in ticks): + return True + elif not any(tick.gridOn for tick in ticks): + return False + else: + return None ax = event.inaxes - # switching on/off a grid in current axes (default key 'g') + # toggle major grids in current axes (default key 'g') + # Both here and below (for 'G'), we do nothing is the grids are not in a + # uniform state, to avoid messing up user customization. if event.key in grid_keys: - # If either major grid is on, turn all major and minor grids off. - if any(tick.gridOn - for tick in ax.xaxis.majorTicks + ax.yaxis.majorTicks): - ax.grid(False, which="both") - # Otherwise, turn the major grids on. + x_state = _get_uniform_gridstate(ax.xaxis.majorTicks) + y_state = _get_uniform_gridstate(ax.yaxis.majorTicks) + cycle = [(False, False), (True, False), (True, True), (False, True)] + try: + x_state, y_state = ( + cycle[(cycle.index((x_state, y_state)) + 1) % len(cycle)]) + except ValueError: + # Exclude major grids not in a uniform state. + pass else: - ax.grid(True) - canvas.draw() - if event.key in grid_minor_keys: - # If either minor grid is on, turn all minor grids off. - if any(tick.gridOn - for tick in ax.xaxis.minorTicks + ax.yaxis.minorTicks): - ax.grid(False, which="minor") - # Otherwise, turn all major and minor grids on. + ax.grid(x_state, which="major", axis="x") + ax.grid(y_state, which="major", axis="y") + canvas.draw_idle() + # toggle major and minor grids in current axes (default key 'G') + if (event.key in grid_minor_keys + # Exclude major grids not in a uniform state. + and None not in [_get_uniform_gridstate(ax.xaxis.majorTicks), + _get_uniform_gridstate(ax.yaxis.majorTicks)]): + x_state = _get_uniform_gridstate(ax.xaxis.minorTicks) + y_state = _get_uniform_gridstate(ax.yaxis.minorTicks) + cycle = [(False, False), (True, False), (True, True), (False, True)] + try: + x_state, y_state = ( + cycle[(cycle.index((x_state, y_state)) + 1) % len(cycle)]) + except ValueError: + # Exclude minor grids not in a uniform state. + pass else: - ax.grid(True, which="both") - canvas.draw() + ax.grid(x_state, which="both", axis="x") + ax.grid(y_state, which="both", axis="y") + canvas.draw_idle() # toggle scaling of y-axes between 'log and 'linear' (default key 'l') elif event.key in toggle_yscale_keys: scale = ax.get_yscale() if scale == 'log': ax.set_yscale('linear') - ax.figure.canvas.draw() + ax.figure.canvas.draw_idle() elif scale == 'linear': ax.set_yscale('log') - ax.figure.canvas.draw() + ax.figure.canvas.draw_idle() # toggle scaling of x-axes between 'log and 'linear' (default key 'k') elif event.key in toggle_xscale_keys: scalex = ax.get_xscale() if scalex == 'log': ax.set_xscale('linear') - ax.figure.canvas.draw() + ax.figure.canvas.draw_idle() elif scalex == 'linear': ax.set_xscale('log') - ax.figure.canvas.draw() + ax.figure.canvas.draw_idle() - elif (event.key.isdigit() and event.key != '0') or event.key in all: + elif (event.key.isdigit() and event.key != '0') or event.key in all_keys: # 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 From 32187dff0a662171dfe84c14c07508c9b0644dad Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 2 Aug 2016 11:27:14 -0700 Subject: [PATCH 3/4] Toolmanager implementation of minor grid toggler. --- lib/matplotlib/backend_tools.py | 75 +++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index c896689bc610..cd290e9db445 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -399,24 +399,74 @@ def trigger(self, sender, event, data=None): a.set_navigate(i == n) -class ToolGrid(ToolToggleBase): - """Tool to toggle the grid of the figure""" +class _ToolGridBase(ToolBase): + """Common functionality between ToolGrid and ToolMinorGrid. + """ - description = 'Toogle Grid' - default_keymap = rcParams['keymap.grid'] + _cycle = [(False, False), (True, False), (True, True), (False, True)] def trigger(self, sender, event, data=None): - if event.inaxes is None: + ax = event.inaxes + if ax is None: return - ToolToggleBase.trigger(self, sender, event, data) + try: + x_state, y_state, which = self._get_next_grid_states(ax) + except ValueError: + pass + else: + ax.grid(x_state, which=which, axis="x") + ax.grid(y_state, which=which, axis="y") + ax.figure.canvas.draw_idle() - def enable(self, event): - event.inaxes.grid(True) - self.figure.canvas.draw_idle() + @staticmethod + def _get_uniform_grid_state(ticks): + """Check whether all grid lines are in the same visibility state. - def disable(self, event): - event.inaxes.grid(False) - self.figure.canvas.draw_idle() + Returns True/False if all grid lines are on or off, None if they are + not all in the same state. + """ + if all(tick.gridOn for tick in ticks): + return True + elif not any(tick.gridOn for tick in ticks): + return False + else: + return None + + +class ToolGrid(_ToolGridBase): + """Tool to toggle the major grids of the figure""" + + description = 'Toogle major grids' + default_keymap = rcParams['keymap.grid'] + + def _get_next_grid_states(self, ax): + x_state, y_state = map(self._get_uniform_grid_state, + [ax.xaxis.majorTicks, ax.yaxis.majorTicks]) + cycle = self._cycle + # Bail out if major grids are not in a uniform state. + x_state, y_state = ( + cycle[(cycle.index((x_state, y_state)) + 1) % len(cycle)]) + return x_state, y_state, "major" + + +class ToolMinorGrid(_ToolGridBase): + """Tool to toggle the major and minor grids of the figure""" + + description = 'Toogle major and minor grids' + default_keymap = rcParams['keymap.grid_minor'] + + def _get_next_grid_states(self, ax): + if None in map(self._get_uniform_grid_state, + [ax.xaxis.majorTicks, ax.yaxis.majorTicks]): + # Bail out if major grids are not in a uniform state. + raise ValueError + x_state, y_state = map(self._get_uniform_grid_state, + [ax.xaxis.minorTicks, ax.yaxis.minorTicks]) + cycle = self._cycle + # Bail out if minor grids are not in a uniform state. + x_state, y_state = ( + cycle[(cycle.index((x_state, y_state)) + 1) % len(cycle)]) + return x_state, y_state, "both" class ToolFullScreen(ToolToggleBase): @@ -944,6 +994,7 @@ def _mouse_move(self, event): 'subplots': 'ToolConfigureSubplots', 'save': 'ToolSaveFigure', 'grid': ToolGrid, + 'grid_minor': ToolMinorGrid, 'fullscreen': ToolFullScreen, 'quit': ToolQuit, 'quit_all': ToolQuitAll, From 0658c2b842d77732690d98815e107b5186be1026 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 9 Aug 2016 09:57:29 -0700 Subject: [PATCH 4/4] Minor fixes re: grid_minor keybinding. --- doc/users/navigation_toolbar.rst | 4 ++-- lib/matplotlib/backend_tools.py | 10 +++++----- matplotlibrc.template | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/users/navigation_toolbar.rst b/doc/users/navigation_toolbar.rst index 43bacbf02f8c..3722f2efe615 100644 --- a/doc/users/navigation_toolbar.rst +++ b/doc/users/navigation_toolbar.rst @@ -99,8 +99,8 @@ Close all plots **shift** + **w** Constrain pan/zoom to x axis hold **x** when panning/zooming with mouse Constrain pan/zoom to y axis hold **y** when panning/zooming with mouse Preserve aspect ratio hold **CONTROL** when panning/zooming with mouse -Toggle major grid **g** when mouse is over an axes -Toggle major and minor grid **G** when mouse is over an axes +Toggle major grids **g** when mouse is over an axes +Toggle minor grids **G** when mouse is over an axes Toggle x axis scale (log/linear) **L** or **k** when mouse is over an axes Toggle y axis scale (log/linear) **l** when mouse is over an axes ================================== ================================================= diff --git a/lib/matplotlib/backend_tools.py b/lib/matplotlib/backend_tools.py index cd290e9db445..73db242638cd 100644 --- a/lib/matplotlib/backend_tools.py +++ b/lib/matplotlib/backend_tools.py @@ -400,8 +400,7 @@ def trigger(self, sender, event, data=None): class _ToolGridBase(ToolBase): - """Common functionality between ToolGrid and ToolMinorGrid. - """ + """Common functionality between ToolGrid and ToolMinorGrid.""" _cycle = [(False, False), (True, False), (True, True), (False, True)] @@ -420,7 +419,8 @@ def trigger(self, sender, event, data=None): @staticmethod def _get_uniform_grid_state(ticks): - """Check whether all grid lines are in the same visibility state. + """ + Check whether all grid lines are in the same visibility state. Returns True/False if all grid lines are on or off, None if they are not all in the same state. @@ -443,7 +443,7 @@ def _get_next_grid_states(self, ax): x_state, y_state = map(self._get_uniform_grid_state, [ax.xaxis.majorTicks, ax.yaxis.majorTicks]) cycle = self._cycle - # Bail out if major grids are not in a uniform state. + # Bail out (via ValueError) if major grids are not in a uniform state. x_state, y_state = ( cycle[(cycle.index((x_state, y_state)) + 1) % len(cycle)]) return x_state, y_state, "major" @@ -463,7 +463,7 @@ def _get_next_grid_states(self, ax): x_state, y_state = map(self._get_uniform_grid_state, [ax.xaxis.minorTicks, ax.yaxis.minorTicks]) cycle = self._cycle - # Bail out if minor grids are not in a uniform state. + # Bail out (via ValueError) if minor grids are not in a uniform state. x_state, y_state = ( cycle[(cycle.index((x_state, y_state)) + 1) % len(cycle)]) return x_state, y_state, "both" diff --git a/matplotlibrc.template b/matplotlibrc.template index 91566d6bdbf1..faf5d2de6876 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -584,8 +584,8 @@ backend : $TEMPLATE_BACKEND #keymap.zoom : o # zoom mnemonic #keymap.save : s # saving current figure #keymap.quit : ctrl+w, cmd+w # close the current figure -#keymap.grid : g # switching on/off a grid in current axes -#keymap.grid_minor : G # switching on/off a grid in current axes +#keymap.grid : g # switching on/off major grids in current axes +#keymap.grid_minor : G # switching on/off minor grids in current axes #keymap.yscale : l # toggle scaling of y-axes ('log'/'linear') #keymap.xscale : L, k # toggle scaling of x-axes ('log'/'linear') #keymap.all_axes : a # enable all axes