Skip to content

Commit 2334c42

Browse files
committed
MVP for using style sheets
1 parent e105fed commit 2334c42

File tree

5 files changed

+76
-37
lines changed

5 files changed

+76
-37
lines changed

Diff for: MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
include LICENSE
22
include README.md
3+
recursive-include * *.mplstyle
34

45
recursive-exclude * __pycache__
56
recursive-exclude * *.py[co]

Diff for: src/napari_matplotlib/base.py

+48-37
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
FigureCanvas,
99
NavigationToolbar2QT,
1010
)
11+
import matplotlib.style as mplstyle
1112
from matplotlib.figure import Figure
1213
from qtpy.QtGui import QIcon
1314
from qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget
@@ -40,8 +41,11 @@ def __init__(
4041
):
4142
super().__init__(parent=parent)
4243
self.viewer = napari_viewer
44+
self._mpl_style_sheet_path: Optional[Path] = None
4345

44-
self.canvas = FigureCanvas()
46+
# Sets figure.* style
47+
with mplstyle.context(self.mpl_style_sheet_path):
48+
self.canvas = FigureCanvas()
4549

4650
self.canvas.figure.patch.set_facecolor("none")
4751
self.canvas.figure.set_layout_engine("constrained")
@@ -52,7 +56,7 @@ def __init__(
5256
# callback to update when napari theme changed
5357
# TODO: this isn't working completely (see issue #140)
5458
# most of our styling respects the theme change but not all
55-
self.viewer.events.theme.connect(self._on_theme_change)
59+
self.viewer.events.theme.connect(self._on_napari_theme_changed)
5660

5761
self.setLayout(QVBoxLayout())
5862
self.layout().addWidget(self.toolbar)
@@ -63,47 +67,40 @@ def figure(self) -> Figure:
6367
"""Matplotlib figure."""
6468
return self.canvas.figure
6569

70+
@property
71+
def mpl_style_sheet_path(self) -> Path:
72+
"""
73+
Path to the set Matplotlib style sheet.
74+
"""
75+
if self._mpl_style_sheet_path is not None:
76+
return self._mpl_style_sheet_path
77+
elif self._napari_theme_has_light_bg():
78+
return Path(__file__).parent / "styles" / "light.mplstyle"
79+
else:
80+
return Path(__file__).parent / "styles" / "dark.mplstyle"
81+
82+
@mpl_style_sheet_path.setter
83+
def mpl_style_sheet_path(self, path: Path) -> None:
84+
self._mpl_style_sheet_path = Path(path)
85+
6686
def add_single_axes(self) -> None:
6787
"""
6888
Add a single Axes to the figure.
6989
7090
The Axes is saved on the ``.axes`` attribute for later access.
7191
"""
72-
self.axes = self.figure.subplots()
73-
self.apply_napari_colorscheme(self.axes)
74-
75-
def apply_napari_colorscheme(self, ax: Axes) -> None:
76-
"""Apply napari-compatible colorscheme to an Axes."""
77-
# get the foreground colours from current theme
78-
theme = napari.utils.theme.get_theme(self.viewer.theme, as_dict=False)
79-
fg_colour = theme.foreground.as_hex() # fg is a muted contrast to bg
80-
text_colour = theme.text.as_hex() # text is high contrast to bg
81-
82-
# changing color of axes background to transparent
83-
ax.set_facecolor("none")
84-
85-
# changing colors of all axes
86-
for spine in ax.spines:
87-
ax.spines[spine].set_color(fg_colour)
88-
89-
ax.xaxis.label.set_color(text_colour)
90-
ax.yaxis.label.set_color(text_colour)
91-
92-
# changing colors of axes labels
93-
ax.tick_params(axis="x", colors=text_colour)
94-
ax.tick_params(axis="y", colors=text_colour)
95-
96-
def _on_theme_change(self) -> None:
97-
"""Update MPL toolbar and axis styling when `napari.Viewer.theme` is changed.
92+
# Sets axes.* style.
93+
# Does not set any text styling set by axes.* keys
94+
with mplstyle.context(self.mpl_style_sheet_path):
95+
self.axes = self.figure.subplots()
9896

99-
Note:
100-
At the moment we only handle the default 'light' and 'dark' napari themes.
97+
def _on_napari_theme_changed(self):
98+
"""
99+
Called when the napari theme is changed.
101100
"""
102101
self._replace_toolbar_icons()
103-
if self.figure.gca():
104-
self.apply_napari_colorscheme(self.figure.gca())
105102

106-
def _theme_has_light_bg(self) -> bool:
103+
def _napari_theme_has_light_bg(self) -> bool:
107104
"""
108105
Does this theme have a light background?
109106
@@ -124,7 +121,7 @@ def _get_path_to_icon(self) -> Path:
124121
https://github.com/matplotlib/matplotlib/tree/main/lib/matplotlib/mpl-data/images
125122
"""
126123
icon_root = Path(__file__).parent / "icons"
127-
if self._theme_has_light_bg():
124+
if self._napari_theme_has_light_bg():
128125
return icon_root / "black"
129126
else:
130127
return icon_root / "white"
@@ -211,6 +208,17 @@ def current_z(self) -> int:
211208
"""
212209
return self.viewer.dims.current_step[0]
213210

211+
def _on_napari_theme_changed(self) -> None:
212+
"""Update MPL toolbar and axis styling when `napari.Viewer.theme` is changed.
213+
214+
Note:
215+
At the moment we only handle the default 'light' and 'dark' napari themes.
216+
"""
217+
super()._on_napari_theme_changed()
218+
self.clear()
219+
self.draw()
220+
221+
214222
def _setup_callbacks(self) -> None:
215223
"""
216224
Sets up callbacks.
@@ -240,12 +248,14 @@ def _draw(self) -> None:
240248
Clear current figure, check selected layers are correct, and draw new
241249
figure if so.
242250
"""
243-
self.clear()
251+
# Clearing axes sets new defaults, so need to make sure style is applied when
252+
# this happens
253+
with mplstyle.context(self.mpl_style_sheet_path):
254+
self.clear()
244255
if self.n_selected_layers in self.n_layers_input and all(
245256
isinstance(layer, self.input_layer_types) for layer in self.layers
246257
):
247258
self.draw()
248-
self.apply_napari_colorscheme(self.figure.gca())
249259
self.canvas.draw()
250260

251261
def clear(self) -> None:
@@ -288,7 +298,8 @@ def clear(self) -> None:
288298
"""
289299
Clear the axes.
290300
"""
291-
self.axes.clear()
301+
with mplstyle.context(self.mpl_style_sheet_path):
302+
self.axes.clear()
292303

293304

294305
class NapariNavigationToolbar(NavigationToolbar2QT):

Diff for: src/napari_matplotlib/styles/README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
This folder contains default built-in Matplotlib style sheets.
2+
See https://matplotlib.org/stable/tutorials/introductory/customizing.html#defining-your-own-style
3+
for more info on Matplotlib style sheets.

Diff for: src/napari_matplotlib/styles/dark.mplstyle

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Dark-theme napari colour scheme for matplotlib plots
2+
3+
# text (very light grey - almost white): #f0f1f2
4+
# foreground (mid grey): #414851
5+
# background (dark blue-gray): #262930
6+
7+
figure.facecolor : none
8+
axes.labelcolor : f0f1f2
9+
axes.facecolor : none
10+
axes.edgecolor : 414851
11+
xtick.color : f0f1f2
12+
ytick.color : f0f1f2

Diff for: src/napari_matplotlib/styles/light.mplstyle

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Light-theme napari colour scheme for matplotlib plots
2+
3+
# text (): #3b3a39
4+
# foreground (): #d6d0ce
5+
# background (): #efebe9
6+
7+
figure.facecolor : none
8+
axes.labelcolor : 3b3a39
9+
axes.facecolor : none
10+
axes.edgecolor : d6d0ce
11+
xtick.color : 3b3a39
12+
ytick.color : 3b3a39

0 commit comments

Comments
 (0)