1
1
import os
2
+ import warnings
2
3
from pathlib import Path
3
4
from typing import List , Optional , Tuple
4
5
5
6
import matplotlib .style
6
7
import napari
7
- from matplotlib .axes import Axes
8
8
from matplotlib .backends .backend_qtagg import (
9
9
FigureCanvas ,
10
10
NavigationToolbar2QT ,
@@ -41,12 +41,8 @@ def __init__(
41
41
):
42
42
super ().__init__ (parent = parent )
43
43
self .viewer = napari_viewer
44
-
45
- has_mpl_stylesheet = self ._apply_user_stylesheet_if_present ()
44
+ self .apply_style ()
46
45
self .canvas = FigureCanvas ()
47
-
48
- if not has_mpl_stylesheet :
49
- self .canvas .figure .patch .set_facecolor ("none" )
50
46
self .canvas .figure .set_layout_engine ("constrained" )
51
47
self .toolbar = NapariNavigationToolbar (
52
48
self .canvas , parent = self
@@ -73,47 +69,42 @@ def add_single_axes(self) -> None:
73
69
The Axes is saved on the ``.axes`` attribute for later access.
74
70
"""
75
71
self .axes = self .figure .subplots ()
76
- self .apply_style (self .axes )
77
72
78
- def apply_style (self , ax : Axes ) -> None :
73
+ def apply_style (self ) -> None :
79
74
"""
80
- Use the user-supplied stylesheet if present , otherwise apply the
81
- napari-compatible colorscheme (theme-dependent) to an Axes .
75
+ Any user-supplied stylesheet takes highest precidence , otherwise apply
76
+ the napari-compatible colorscheme (depends on the napari theme) .
82
77
"""
83
78
if self ._apply_user_stylesheet_if_present ():
84
79
return
85
80
86
- # get the foreground colours from current theme
87
- theme = napari .utils .theme .get_theme (self .viewer .theme , as_dict = False )
88
- fg_colour = theme .foreground .as_hex () # fg is a muted contrast to bg
89
- text_colour = theme .text .as_hex () # text is high contrast to bg
90
-
91
- # changing color of axes background to transparent
92
- ax .set_facecolor ("none" )
93
-
94
- # changing colors of all axes
95
- for spine in ax .spines :
96
- ax .spines [spine ].set_color (fg_colour )
97
-
98
- ax .xaxis .label .set_color (text_colour )
99
- ax .yaxis .label .set_color (text_colour )
100
-
101
- # changing colors of axes labels
102
- ax .tick_params (axis = "x" , colors = text_colour )
103
- ax .tick_params (axis = "y" , colors = text_colour )
81
+ stylesheet_dir = self ._get_path_to_mpl_stylesheets ()
82
+ if self .viewer .theme == "dark" :
83
+ matplotlib .style .use (stylesheet_dir / "napari-dark.mplstyle" )
84
+ elif self .viewer .theme == "light" :
85
+ matplotlib .style .use (stylesheet_dir / "napari-light.mplstyle" )
86
+ else :
87
+ warnings .warn (
88
+ f"Napari theme '{ self .viewer .theme } ' is not supported by"
89
+ " napari-matplotlib. Will fall back to the matplotlib default."
90
+ )
91
+ matplotlib .style .use ("default" )
92
+ return
104
93
105
94
def _apply_user_stylesheet_if_present (self ) -> bool :
106
95
"""
107
96
Apply the user-supplied stylesheet if present.
108
97
109
98
Returns
110
99
-------
111
- True if the stylesheet was present and applied.
112
- False otherwise.
100
+ True if the stylesheet was present and applied.
101
+ False otherwise.
113
102
"""
114
103
if (Path .cwd () / "user.mplstyle" ).exists ():
115
104
matplotlib .style .use ("./user.mplstyle" )
116
105
return True
106
+ # TODO: can put more complicated stuff in here. Like a config dir,
107
+ # or take a given named file from the matplotlib user styles
117
108
return False
118
109
119
110
def _on_theme_change (self ) -> None :
@@ -123,8 +114,8 @@ def _on_theme_change(self) -> None:
123
114
At the moment we only handle the default 'light' and 'dark' napari themes.
124
115
"""
125
116
self ._replace_toolbar_icons ()
126
- if self .figure . gca ():
127
- self .apply_style ( self . figure . gca () )
117
+ self .apply_style ()
118
+ # self.canvas.reload( )
128
119
129
120
def _theme_has_light_bg (self ) -> bool :
130
121
"""
@@ -139,6 +130,9 @@ def _theme_has_light_bg(self) -> bool:
139
130
_ , _ , bg_lightness = theme .background .as_hsl_tuple ()
140
131
return bg_lightness > 0.5
141
132
133
+ def _get_path_to_mpl_stylesheets (self ) -> Path :
134
+ return Path (__file__ ).parent / "stylesheets"
135
+
142
136
def _get_path_to_icon (self ) -> Path :
143
137
"""
144
138
Get the icons directory (which is theme-dependent).
@@ -268,7 +262,7 @@ def _draw(self) -> None:
268
262
isinstance (layer , self .input_layer_types ) for layer in self .layers
269
263
):
270
264
self .draw ()
271
- self .apply_style (self . figure . gca () )
265
+ self .apply_style ()
272
266
self .canvas .draw ()
273
267
274
268
def clear (self ) -> None :
0 commit comments