Skip to content

Custom stylesheets #181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ To use these:
1. Open the desired widget using the ``Plugins > napari-matplotlib`` menu in napari.
2. Select a single layer that has a features table using the napari layers list in the bottom left-hand side of the window.
3. Use the drop down menu(s) under the Matplotlib figure to select the feature(s) to plot.

Customising plots
-----------------
`Matplotlib style sheets <https://matplotlib.org/stable/tutorials/introductory/customizing.html#defining-your-own-style>`__ can be used to customise
the plots generated by ``napari-matplotlib``.
To use a custom style sheet:

1. Save it as ``napari-matplotlib.mplstyle``
2. Put it in the Matplotlib configuration directory.
The location of this directory varies on different computers,
and can be found by calling :func:`matplotlib.get_configdir()`.
8 changes: 7 additions & 1 deletion src/napari_matplotlib/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path
from typing import List, Optional, Tuple

import matplotlib
import matplotlib.style as mplstyle
import napari
from matplotlib.backends.backend_qtagg import (
Expand All @@ -16,6 +17,10 @@

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

_CUSTOM_STYLE_PATH = (
Path(matplotlib.get_configdir()) / "napari-matplotlib.mplstyle"
)


class BaseNapariMPLWidget(QWidget):
"""
Expand Down Expand Up @@ -46,7 +51,6 @@ def __init__(
with mplstyle.context(self.mpl_style_sheet_path):
self.canvas = FigureCanvas()

self.canvas.figure.patch.set_facecolor("none")
self.canvas.figure.set_layout_engine("constrained")
self.toolbar = NapariNavigationToolbar(
self.canvas, parent=self
Expand All @@ -73,6 +77,8 @@ def mpl_style_sheet_path(self) -> Path:
"""
if self._mpl_style_sheet_path is not None:
return self._mpl_style_sheet_path
elif (_CUSTOM_STYLE_PATH).exists():
return _CUSTOM_STYLE_PATH
elif self._napari_theme_has_light_bg():
return Path(__file__).parent / "styles" / "light.mplstyle"
else:
Expand Down
50 changes: 49 additions & 1 deletion src/napari_matplotlib/tests/test_theme.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import os
import shutil
from copy import deepcopy
from pathlib import Path

import matplotlib
import napari
import numpy as np
import pytest
from matplotlib.colors import to_rgba

from napari_matplotlib import ScatterWidget
from napari_matplotlib import HistogramWidget, ScatterWidget
from napari_matplotlib.base import NapariMPLWidget


Expand Down Expand Up @@ -140,3 +145,46 @@ def test_custom_theme(make_napari_viewer, theme_path, brain_data):
viewer.layers.selection.add(viewer.layers[1])

return deepcopy(widget.figure)


def find_mpl_stylesheet(name: str) -> Path:
"""Find the built-in matplotlib stylesheet."""
return Path(matplotlib.__path__[0]) / f"mpl-data/stylelib/{name}.mplstyle"


def test_custom_stylesheet(make_napari_viewer, image_data):
"""
Test that a stylesheet in the current directory is given precidence.

Do this by copying over a stylesheet from matplotlib's built in styles,
naming it correctly, and checking the colours are as expected.
"""
# Copy Solarize_Light2 as if it was a user-overriden stylesheet.
style_sheet_path = (
Path(matplotlib.get_configdir()) / "napari-matplotlib.mplstyle"
)
if style_sheet_path.exists():
pytest.skip("Won't ovewrite existing custom style sheet.")
shutil.copy(
find_mpl_stylesheet("Solarize_Light2"),
style_sheet_path,
)

try:
viewer = make_napari_viewer()
viewer.add_image(image_data[0], **image_data[1])
widget = HistogramWidget(viewer)
assert widget.mpl_style_sheet_path == style_sheet_path
ax = widget.figure.gca()

# The axes should have a light brownish grey background:
assert ax.get_facecolor() == to_rgba("#eee8d5")
assert ax.patch.get_facecolor() == to_rgba("#eee8d5")

# The figure background and axis gridlines are light yellow:
assert widget.figure.patch.get_facecolor() == to_rgba("#fdf6e3")
for gridline in ax.get_xgridlines() + ax.get_ygridlines():
assert gridline.get_visible() is True
assert gridline.get_color() == "#fdf6e3"
finally:
os.remove(style_sheet_path)