Skip to content

Commit c481207

Browse files
authored
Merge pull request #237 from Chris-N-K/theme_detection_update
Theme detection and update improvements
2 parents cf1b129 + 2d1f804 commit c481207

20 files changed

+87
-167
lines changed

MANIFEST.in

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
include LICENSE
22
include README.md
3-
recursive-include * *.mplstyle
43

54
recursive-exclude * __pycache__
65
recursive-exclude * *.py[co]

docs/user_guide.rst

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,6 @@ To use these:
4040

4141
Customising plots
4242
-----------------
43-
`Matplotlib style sheets <https://matplotlib.org/stable/tutorials/introductory/customizing.html#defining-your-own-style>`__ can be used to customise
44-
the plots generated by ``napari-matplotlib``.
45-
To use a custom style sheet:
46-
47-
1. Save it as ``napari-matplotlib.mplstyle``
48-
2. Put it in the Matplotlib configuration directory.
49-
The location of this directory varies on different computers,
50-
and can be found by calling :func:`matplotlib.get_configdir()`.
43+
``napari-matplotlib`` uses colours from the current napari theme to customise the
44+
Matplotlib plots. See `the example on creating a new napari theme
45+
<https://napari.org/stable/gallery/new_theme.html>`_ for a helpful guide.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ write_to = "src/napari_matplotlib/_version.py"
77

88
[tool.pytest.ini_options]
99
qt_api = "pyqt6"
10-
addopts = "--mpl"
10+
addopts = "--mpl --mpl-baseline-relative"
1111
filterwarnings = [
1212
"error",
1313
# Coming from vispy

src/napari_matplotlib/base.py

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,22 @@
22
from pathlib import Path
33
from typing import Optional
44

5-
import matplotlib
65
import matplotlib.style as mplstyle
76
import napari
87
from matplotlib.backends.backend_qtagg import ( # type: ignore[attr-defined]
98
FigureCanvasQTAgg,
109
NavigationToolbar2QT,
1110
)
1211
from matplotlib.figure import Figure
12+
from napari.utils.events import Event
13+
from napari.utils.theme import get_theme
1314
from qtpy.QtGui import QIcon
1415
from qtpy.QtWidgets import QLabel, QVBoxLayout, QWidget
1516

16-
from .util import Interval, from_napari_css_get_size_of
17+
from .util import Interval, from_napari_css_get_size_of, style_sheet_from_theme
1718

1819
__all__ = ["BaseNapariMPLWidget", "NapariMPLWidget", "SingleAxesWidget"]
1920

20-
_CUSTOM_STYLE_PATH = (
21-
Path(matplotlib.get_configdir()) / "napari-matplotlib.mplstyle"
22-
)
23-
2421

2522
class BaseNapariMPLWidget(QWidget):
2623
"""
@@ -45,18 +42,17 @@ def __init__(
4542
):
4643
super().__init__(parent=parent)
4744
self.viewer = napari_viewer
48-
self._mpl_style_sheet_path: Optional[Path] = None
45+
self.napari_theme_style_sheet = style_sheet_from_theme(
46+
get_theme(napari_viewer.theme, as_dict=False)
47+
)
4948

5049
# Sets figure.* style
51-
with mplstyle.context(self.mpl_style_sheet_path):
50+
with mplstyle.context(self.napari_theme_style_sheet):
5251
self.canvas = FigureCanvasQTAgg() # type: ignore[no-untyped-call]
5352

5453
self.canvas.figure.set_layout_engine("constrained")
5554
self.toolbar = NapariNavigationToolbar(self.canvas, parent=self)
5655
self._replace_toolbar_icons()
57-
# callback to update when napari theme changed
58-
# TODO: this isn't working completely (see issue #140)
59-
# most of our styling respects the theme change but not all
6056
self.viewer.events.theme.connect(self._on_napari_theme_changed)
6157

6258
self.setLayout(QVBoxLayout())
@@ -68,24 +64,6 @@ def figure(self) -> Figure:
6864
"""Matplotlib figure."""
6965
return self.canvas.figure
7066

71-
@property
72-
def mpl_style_sheet_path(self) -> Path:
73-
"""
74-
Path to the set Matplotlib style sheet.
75-
"""
76-
if self._mpl_style_sheet_path is not None:
77-
return self._mpl_style_sheet_path
78-
elif (_CUSTOM_STYLE_PATH).exists():
79-
return _CUSTOM_STYLE_PATH
80-
elif self._napari_theme_has_light_bg():
81-
return Path(__file__).parent / "styles" / "light.mplstyle"
82-
else:
83-
return Path(__file__).parent / "styles" / "dark.mplstyle"
84-
85-
@mpl_style_sheet_path.setter
86-
def mpl_style_sheet_path(self, path: Path) -> None:
87-
self._mpl_style_sheet_path = Path(path)
88-
8967
def add_single_axes(self) -> None:
9068
"""
9169
Add a single Axes to the figure.
@@ -94,13 +72,21 @@ def add_single_axes(self) -> None:
9472
"""
9573
# Sets axes.* style.
9674
# Does not set any text styling set by axes.* keys
97-
with mplstyle.context(self.mpl_style_sheet_path):
75+
with mplstyle.context(self.napari_theme_style_sheet):
9876
self.axes = self.figure.add_subplot()
9977

100-
def _on_napari_theme_changed(self) -> None:
78+
def _on_napari_theme_changed(self, event: Event) -> None:
10179
"""
10280
Called when the napari theme is changed.
81+
82+
Parameters
83+
----------
84+
event : napari.utils.events.Event
85+
Event that triggered the callback.
10386
"""
87+
self.napari_theme_style_sheet = style_sheet_from_theme(
88+
get_theme(event.value, as_dict=False)
89+
)
10490
self._replace_toolbar_icons()
10591

10692
def _napari_theme_has_light_bg(self) -> bool:
@@ -211,15 +197,18 @@ def current_z(self) -> int:
211197
"""
212198
return self.viewer.dims.current_step[0]
213199

214-
def _on_napari_theme_changed(self) -> None:
200+
def _on_napari_theme_changed(self, event: Event) -> None:
215201
"""Update MPL toolbar and axis styling when `napari.Viewer.theme` is changed.
216202
217-
Note:
218-
At the moment we only handle the default 'light' and 'dark' napari themes.
203+
Parameters
204+
----------
205+
event : napari.utils.events.Event
206+
Event that triggered the callback.
219207
"""
220-
super()._on_napari_theme_changed()
221-
self.clear()
222-
self.draw()
208+
super()._on_napari_theme_changed(event)
209+
# use self._draw instead of self.draw to cope with redraw while there are no
210+
# layers, this makes the self.clear() obsolete
211+
self._draw()
223212

224213
def _setup_callbacks(self) -> None:
225214
"""
@@ -252,13 +241,15 @@ def _draw(self) -> None:
252241
"""
253242
# Clearing axes sets new defaults, so need to make sure style is applied when
254243
# this happens
255-
with mplstyle.context(self.mpl_style_sheet_path):
244+
with mplstyle.context(self.napari_theme_style_sheet):
245+
# everything should be done in the style context
256246
self.clear()
257-
if self.n_selected_layers in self.n_layers_input and all(
258-
isinstance(layer, self.input_layer_types) for layer in self.layers
259-
):
260-
self.draw()
261-
self.canvas.draw() # type: ignore[no-untyped-call]
247+
if self.n_selected_layers in self.n_layers_input and all(
248+
isinstance(layer, self.input_layer_types)
249+
for layer in self.layers
250+
):
251+
self.draw()
252+
self.canvas.draw() # type: ignore[no-untyped-call]
262253

263254
def clear(self) -> None:
264255
"""
@@ -300,7 +291,7 @@ def clear(self) -> None:
300291
"""
301292
Clear the axes.
302293
"""
303-
with mplstyle.context(self.mpl_style_sheet_path):
294+
with mplstyle.context(self.napari_theme_style_sheet):
304295
self.axes.clear()
305296

306297

src/napari_matplotlib/styles/README.md

Lines changed: 0 additions & 3 deletions
This file was deleted.

src/napari_matplotlib/styles/dark.mplstyle

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/napari_matplotlib/styles/light.mplstyle

Lines changed: 0 additions & 12 deletions
This file was deleted.
Loading
Loading
Loading

0 commit comments

Comments
 (0)