Skip to content

Commit

Permalink
Merge pull request mantidproject#37438 from mantidproject/37402-short…
Browse files Browse the repository at this point in the history
…cuts-k-and-l-not-scaling-log-axis-correctly

Fix key shortcuts for quickly switching plot axis
  • Loading branch information
SilkeSchomann committed Jun 3, 2024
2 parents 3a3ae96 + c0cdaaf commit dedb209
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/source/release/v6.11.0/Workbench/Bugfixes/37438.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fixed key shortcuts `k` and `l` in mantid plot for quickly switching between `linear` and `log` scales.
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,43 @@ File > Settings
|
|
Useful Key Shortcuts
====================

Mantid plots support multiple key shortcuts by default.
Please note especially the shortcuts `k` and `l`, which are useful for quickly switching between linear and log axes scales.

.. list-table::
:header-rows: 1

* - Action
- Key shortcuts
* - Toggle fullscreen
- f, ctrl+f
* - Reset to homme
- h, r
* - Go back to previous view
- c, backspace
* - Go forward to next view
- v
* - Pan
- p
* - Zoom
- o
* - Save
- s
* - Quit figure
- q, ctrl+w, cmd+w
* - Toggle major grids
- g
* - Toggle minor grids
- G
* - Switch x scale between log/linear
- k
* - Switch y scale between log/linear
- l


**Other Plotting Documentation**

* :ref:`02_scripting_plots`
Expand Down
4 changes: 4 additions & 0 deletions qt/applications/workbench/workbench/plotting/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ def initialize_matplotlib():
mpl.rcParams["figure.dpi"] = QApplication.instance().desktop().physicalDpiX()
# Hide warning made by matplotlib before checking our backend.
warnings.filterwarnings("ignore", message="Starting a Matplotlib GUI outside of the main thread will likely fail.")
# Disabling default key shortcuts for toggling axes scale
mpl.rcParams["keymap.xscale"].remove("k")
mpl.rcParams["keymap.xscale"].remove("L")
mpl.rcParams["keymap.yscale"].remove("l")


def init_mpl_gcf():
Expand Down
21 changes: 21 additions & 0 deletions qt/applications/workbench/workbench/plotting/figureinteraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def __init__(self, fig_manager):
self._cids.append(canvas.mpl_connect("resize_event", self.mpl_redraw_annotations))
self._cids.append(canvas.mpl_connect("figure_leave_event", self.on_leave))
self._cids.append(canvas.mpl_connect("scroll_event", self.on_scroll))
self._cids.append(canvas.mpl_connect("key_press_event", self.on_key_press))

self.canvas = canvas
self.toolbar_manager = ToolbarStateManager(self.canvas.toolbar)
Expand Down Expand Up @@ -144,6 +145,26 @@ def on_scroll(self, event):
self.redraw_annotations()
event.canvas.draw()

def on_key_press(self, event):
ax = event.inaxes
if ax is None or isinstance(ax, Axes3D) or len(ax.get_images()) == 0 and len(ax.get_lines()) == 0:
return

if event.key == "k":
current_xscale = ax.get_xscale()
next_xscale = self._get_next_axis_scale(current_xscale)
self._quick_change_axes((next_xscale, ax.get_yscale()), ax)

if event.key == "l":
current_yscale = ax.get_yscale()
next_yscale = self._get_next_axis_scale(current_yscale)
self._quick_change_axes((ax.get_xscale(), next_yscale), ax)

def _get_next_axis_scale(self, current_scale):
if current_scale == "linear":
return "log"
return "linear"

def on_mouse_button_press(self, event):
"""Respond to a MouseEvent where a button was pressed"""
# local variables to avoid constant self lookup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def test_construction_registers_handler_for_button_press_event(self):
call("resize_event", interactor.mpl_redraw_annotations),
call("figure_leave_event", interactor.on_leave),
call("scroll_event", interactor.on_scroll),
call("key_press_event", interactor.on_key_press),
]
fig_manager.canvas.mpl_connect.assert_has_calls(expected_call)
self.assertEqual(len(expected_call), fig_manager.canvas.mpl_connect.call_count)
Expand Down Expand Up @@ -723,6 +724,38 @@ def test_click_y_axes_tick_label_launches_y_axes_editor(self, mock_y_editor):

mock_y_editor.assert_called_once()

def test_keyboard_shortcut_switch_x_scale(self):
key_press_event = self._create_mock_key_press_event("k")
key_press_event.inaxes.get_xscale.return_value = "linear"
key_press_event.inaxes.get_yscale.return_value = "log"
key_press_event.inaxes.get_xlim.return_value = (0, 100)
key_press_event.inaxes.get_ylim.return_value = (5, 10)
key_press_event.inaxes.get_lines.return_value = ["fake_line"]
fig_manager = MagicMock()
fig_manager.canvas = MagicMock()
interactor = FigureInteraction(fig_manager)
interactor.on_key_press(key_press_event)
key_press_event.inaxes.set_xscale.assert_called_once_with("log")
key_press_event.inaxes.set_yscale.assert_called_once_with("log")
key_press_event.inaxes.set_xlim.assert_called_once_with((0, 100))
key_press_event.inaxes.set_ylim.assert_called_once_with((5, 10))

def test_keyboard_shortcut_switch_y_scale(self):
key_press_event = self._create_mock_key_press_event("l")
key_press_event.inaxes.get_xscale.return_value = "linear"
key_press_event.inaxes.get_yscale.return_value = "log"
key_press_event.inaxes.get_xlim.return_value = (0, 100)
key_press_event.inaxes.get_ylim.return_value = (5, 10)
key_press_event.inaxes.get_lines.return_value = ["fake_line"]
fig_manager = MagicMock()
fig_manager.canvas = MagicMock()
interactor = FigureInteraction(fig_manager)
interactor.on_key_press(key_press_event)
key_press_event.inaxes.set_xscale.assert_called_once_with("linear")
key_press_event.inaxes.set_yscale.assert_called_once_with("linear")
key_press_event.inaxes.set_xlim.assert_called_once_with((0, 100))
key_press_event.inaxes.set_ylim.assert_called_once_with((5, 10))

# Private methods
def _create_mock_fig_manager_to_accept_right_click(self):
fig_manager = MagicMock()
Expand Down Expand Up @@ -761,6 +794,11 @@ def _create_mock_double_left_click(self):
type(mouse_event).dblclick = PropertyMock(return_value=True)
return mouse_event

def _create_mock_key_press_event(self, key):
key_press_event = MagicMock(inaxes=MagicMock(spec=MantidAxes, collections=[], creation_args=[{}]))
type(key_press_event).key = PropertyMock(return_value=key)
return key_press_event

def _create_axes_for_axes_editor_test(self, mouse_over: str):
ax = MagicMock()
ax.xaxis.contains.return_value = (mouse_over == "xaxis", {})
Expand Down

0 comments on commit dedb209

Please sign in to comment.