11
11
from types import MethodType
12
12
from typing import Optional , Sequence , Union
13
13
14
- from matplotlib .backends .backend_qtagg import FigureCanvas
14
+ from matplotlib .backends .backend_qtagg import FigureCanvas , NavigationToolbar2QT
15
15
16
16
import numpy as np
17
17
from napari ._qt .widgets .qt_welcome import QtWelcomeLabel
@@ -296,6 +296,40 @@ def on_close(self, event, widget):
296
296
event .accept ()
297
297
298
298
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
+
299
333
class KeypointMatplotlibCanvas (QWidget ):
300
334
"""
301
335
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):
310
344
self .canvas = FigureCanvas ()
311
345
self .canvas .figure .set_layout_engine ("constrained" )
312
346
self .ax = self .canvas .figure .subplots ()
347
+ self .toolbar = NapariNavigationToolbar (self .canvas , parent = self )
348
+ self ._replace_toolbar_icons ()
313
349
self .canvas .mpl_connect ("button_press_event" , self .on_doubleclick )
314
350
self .vline = self .ax .axvline (0 , 0 , 1 , color = "k" , linestyle = "--" )
315
351
self .ax .set_xlabel ("Frame" )
@@ -328,6 +364,7 @@ def __init__(self, napari_viewer, parent=None):
328
364
329
365
layout = QVBoxLayout ()
330
366
layout .addWidget (self .canvas )
367
+ layout .addWidget (self .toolbar )
331
368
layout2 = QHBoxLayout ()
332
369
layout2 .addWidget (self .slider )
333
370
layout2 .addWidget (self .slider_value )
@@ -383,6 +420,40 @@ def mpl_style_sheet_path(self) -> Path:
383
420
else :
384
421
return Path (__file__ ).parent / "styles" / "dark.mplstyle"
385
422
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
+
386
457
def _load_dataframe (self ):
387
458
points_layer = None
388
459
for layer in self .viewer .layers :
0 commit comments