Skip to content

Commit 6d36e14

Browse files
committed
Add toolbar to matplotlib widget
1 parent c7e6365 commit 6d36e14

21 files changed

+72
-1
lines changed

src/napari_deeplabcut/_widgets.py

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from types import MethodType
1212
from typing import Optional, Sequence, Union
1313

14-
from matplotlib.backends.backend_qtagg import FigureCanvas
14+
from matplotlib.backends.backend_qtagg import FigureCanvas, NavigationToolbar2QT
1515

1616
import numpy as np
1717
from napari._qt.widgets.qt_welcome import QtWelcomeLabel
@@ -296,6 +296,40 @@ def on_close(self, event, widget):
296296
event.accept()
297297

298298

299+
# Class taken from https://github.com/matplotlib/napari-matplotlib/blob/53aa5ec95c1f3901e21dedce8347d3f95efe1f79/src/napari_matplotlib/base.py#L309
300+
class NapariNavigationToolbar(NavigationToolbar2QT):
301+
"""Custom Toolbar style for Napari."""
302+
303+
def __init__(self, *args, **kwargs): # type: ignore[no-untyped-def]
304+
super().__init__(*args, **kwargs)
305+
self.setIconSize(QSize(28, 28))
306+
307+
def _update_buttons_checked(self) -> None:
308+
"""Update toggle tool icons when selected/unselected."""
309+
super()._update_buttons_checked()
310+
icon_dir = self.parentWidget()._get_path_to_icon()
311+
312+
# changes pan/zoom icons depending on state (checked or not)
313+
if "pan" in self._actions:
314+
if self._actions["pan"].isChecked():
315+
self._actions["pan"].setIcon(
316+
QIcon(os.path.join(icon_dir, "Pan_checked.png"))
317+
)
318+
else:
319+
self._actions["pan"].setIcon(
320+
QIcon(os.path.join(icon_dir, "Pan.png"))
321+
)
322+
if "zoom" in self._actions:
323+
if self._actions["zoom"].isChecked():
324+
self._actions["zoom"].setIcon(
325+
QIcon(os.path.join(icon_dir, "Zoom_checked.png"))
326+
)
327+
else:
328+
self._actions["zoom"].setIcon(
329+
QIcon(os.path.join(icon_dir, "Zoom.png"))
330+
)
331+
332+
299333
class KeypointMatplotlibCanvas(QWidget):
300334
"""
301335
Class about matplotlib canvas in which I will draw the keypoints over a range of frames
@@ -310,6 +344,8 @@ def __init__(self, napari_viewer, parent=None):
310344
self.canvas = FigureCanvas()
311345
self.canvas.figure.set_layout_engine("constrained")
312346
self.ax = self.canvas.figure.subplots()
347+
self.toolbar = NapariNavigationToolbar(self.canvas, parent=self)
348+
self._replace_toolbar_icons()
313349
self.canvas.mpl_connect("button_press_event", self.on_doubleclick)
314350
self.vline = self.ax.axvline(0, 0, 1, color="k", linestyle="--")
315351
self.ax.set_xlabel("Frame")
@@ -328,6 +364,7 @@ def __init__(self, napari_viewer, parent=None):
328364

329365
layout = QVBoxLayout()
330366
layout.addWidget(self.canvas)
367+
layout.addWidget(self.toolbar)
331368
layout2 = QHBoxLayout()
332369
layout2.addWidget(self.slider)
333370
layout2.addWidget(self.slider_value)
@@ -383,6 +420,40 @@ def mpl_style_sheet_path(self) -> Path:
383420
else:
384421
return Path(__file__).parent / "styles" / "dark.mplstyle"
385422

423+
def _get_path_to_icon(self) -> Path:
424+
"""
425+
Get the icons directory (which is theme-dependent).
426+
427+
Icons modified from
428+
https://github.com/matplotlib/matplotlib/tree/main/lib/matplotlib/mpl-data/images
429+
"""
430+
icon_root = Path(__file__).parent / "assets"
431+
if self._napari_theme_has_light_bg():
432+
return icon_root / "black"
433+
else:
434+
return icon_root / "white"
435+
436+
def _replace_toolbar_icons(self) -> None:
437+
"""
438+
Modifies toolbar icons to match the napari theme, and add some tooltips.
439+
"""
440+
icon_dir = self._get_path_to_icon()
441+
for action in self.toolbar.actions():
442+
text = action.text()
443+
if text == "Pan":
444+
action.setToolTip(
445+
"Pan/Zoom: Left button pans; Right button zooms; "
446+
"Click once to activate; Click again to deactivate"
447+
)
448+
if text == "Zoom":
449+
action.setToolTip(
450+
"Zoom to rectangle; Click once to activate; "
451+
"Click again to deactivate"
452+
)
453+
if len(text) > 0: # i.e. not a separator item
454+
icon_path = os.path.join(icon_dir, text + ".png")
455+
action.setIcon(QIcon(icon_path))
456+
386457
def _load_dataframe(self):
387458
points_layer = None
388459
for layer in self.viewer.layers:
6.76 KB
Loading
7.08 KB
Loading
6.65 KB
Loading
7.24 KB
Loading
7.14 KB
Loading
12.1 KB
Loading
7.54 KB
Loading
6.88 KB
Loading
8.17 KB
Loading

0 commit comments

Comments
 (0)