Files
geMoldInsight/venv/lib/python3.11/site-packages/pyvista/plotting/themes.py
T
2026-02-12 23:27:48 +08:00

3498 lines
100 KiB
Python

"""API description for managing plotting theme parameters in pyvista.
Examples
--------
Apply a built-in theme
>>> import pyvista as pv
>>> pv.set_plot_theme('default')
>>> pv.set_plot_theme('document')
>>> pv.set_plot_theme('dark')
>>> pv.set_plot_theme('paraview')
Load a theme into pyvista
>>> from pyvista.plotting.themes import DocumentTheme
>>> theme = DocumentTheme()
>>> theme.save('my_theme.json') # doctest:+SKIP
>>> loaded_theme = pv.load_theme('my_theme.json') # doctest:+SKIP
Create a custom theme from the default theme and load it into
pyvista.
>>> my_theme = DocumentTheme()
>>> my_theme.font.size = 20
>>> my_theme.font.title_size = 40
>>> my_theme.cmap = 'jet'
>>> pv.global_theme.load_theme(my_theme)
>>> pv.global_theme.font.size
20
"""
from __future__ import annotations
from enum import Enum
from itertools import chain
import json
import os
import pathlib
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
import warnings
import pyvista # noqa: TC001
from pyvista.core.utilities.misc import _check_range
from .colors import Color
from .colors import get_cmap_safe
from .colors import get_cycler
from .opts import InterpolationType
from .tools import parse_font_family
if TYPE_CHECKING:
from collections.abc import Callable
from pyvista.core._typing_core import VectorLike
from pyvista.jupyter import JupyterBackendOptions
from ._typing import ColorLike
from ._typing import ColormapOptions
def _set_plot_theme_from_env() -> None:
"""Set plot theme from an environment variable."""
if 'PYVISTA_PLOT_THEME' in os.environ:
try:
theme = os.environ['PYVISTA_PLOT_THEME']
set_plot_theme(theme.lower())
except ValueError:
allowed = ', '.join([item.name for item in _NATIVE_THEMES])
warnings.warn(
f'\n\nInvalid PYVISTA_PLOT_THEME environment variable "{theme}". '
f'Should be one of the following: {allowed}',
)
def load_theme(filename):
"""Load a theme from a file.
Parameters
----------
filename : str
Theme file. Must be json.
Returns
-------
pyvista.Theme
The loaded theme.
Examples
--------
>>> import pyvista as pv
>>> from pyvista.plotting.themes import DocumentTheme
>>> theme = DocumentTheme()
>>> theme.save('my_theme.json') # doctest:+SKIP
>>> loaded_theme = pv.load_theme('my_theme.json') # doctest:+SKIP
"""
with Path(filename).open() as f:
theme_dict = json.load(f)
return Theme.from_dict(theme_dict)
def set_plot_theme(theme):
"""Set the plotting parameters to a predefined theme using a string.
Parameters
----------
theme : str
Theme name. Either ``'default'``, ``'document'``, ``'dark'``,
or ``'paraview'``.
Examples
--------
Set to the default theme.
>>> import pyvista as pv
>>> pv.set_plot_theme('default')
Set to the document theme.
>>> pv.set_plot_theme('document')
Set to the dark theme.
>>> pv.set_plot_theme('dark')
Set to the ParaView theme.
>>> pv.set_plot_theme('paraview')
"""
import pyvista # noqa: PLC0415
if isinstance(theme, str):
theme = theme.lower()
try:
new_theme_type = _NATIVE_THEMES[theme].value
except KeyError:
msg = f"Theme {theme} not found in PyVista's native themes."
raise ValueError(msg)
pyvista.global_theme.load_theme(new_theme_type())
elif isinstance(theme, Theme):
pyvista.global_theme.load_theme(theme)
else:
msg = (
f'Expected a ``pyvista.plotting.themes.Theme`` or ``str``, not {type(theme).__name__}'
)
raise TypeError(msg)
# Mostly from https://stackoverflow.com/questions/56579348/how-can-i-force-subclasses-to-have-slots
class _ForceSlots(type):
"""Metaclass to force classes and subclasses to have __slots__."""
@classmethod
def __prepare__(cls, name, bases, **kwargs): # type: ignore[override]
super_prepared = super().__prepare__(cls, name, bases, **kwargs) # type: ignore[arg-type, call-arg, misc]
super_prepared['__slots__'] = ()
return super_prepared
class _ThemeConfig(metaclass=_ForceSlots):
"""Provide common methods for theme configuration classes."""
__slots__: list[str] = []
@classmethod
def from_dict(cls, dict_):
"""Create from a dictionary."""
inst = cls()
for key, value in dict_.items():
attr = getattr(inst, key)
if hasattr(attr, 'from_dict'):
setattr(inst, key, attr.from_dict(value))
else:
setattr(inst, key, value)
return inst
def to_dict(self) -> dict[str, Any]:
"""Return theme config parameters as a dictionary.
Returns
-------
dict
This theme parameter represented as a dictionary.
"""
# remove the first underscore in each entry
dict_ = {}
for key in self._all__slots__():
value = getattr(self, key)
key_ = key[1:]
if hasattr(value, 'to_dict'):
dict_[key_] = value.to_dict()
else:
dict_[key_] = value
return dict_
def __eq__(self, other) -> bool:
if not isinstance(other, _ThemeConfig):
return False
for attr_name in other._all__slots__():
attr = getattr(self, attr_name)
other_attr = getattr(other, attr_name)
if (
isinstance(attr, (tuple, list)) and tuple(attr) != tuple(other_attr)
) or not attr == other_attr:
return False
return True
__hash__ = None # type: ignore[assignment] # https://github.com/pyvista/pyvista/pull/7671
def __getitem__(self, key):
"""Get a value via a key.
Implemented here for backwards compatibility.
"""
return getattr(self, key)
def __setitem__(self, key, value):
"""Set a value via a key.
Implemented here for backwards compatibility.
"""
setattr(self, key, value)
@classmethod
def _all__slots__(cls):
"""Get all slots including parent classes."""
mro = cls.mro()
return tuple(chain.from_iterable(c.__slots__ for c in mro if c is not object)) # type: ignore[attr-defined]
class _LightingConfig(_ThemeConfig):
"""PyVista lighting configuration.
This will control the lighting interpolation type, parameters,
and Physically Based Rendering (PBR) options
Examples
--------
Set global PBR parameters.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.interpolation = 'pbr'
>>> pv.global_theme.lighting_params.metallic = 0.5
>>> pv.global_theme.lighting_params.roughness = 0.25
"""
__slots__ = [
'_ambient',
'_diffuse',
'_emissive',
'_interpolation',
'_metallic',
'_roughness',
'_specular',
'_specular_power',
]
def __init__(self):
self._interpolation = InterpolationType.FLAT.value
self._metallic = 0.0
self._roughness = 0.5
self._ambient = 0.0
self._diffuse = 1.0
self._specular = 0.0
self._specular_power = 100.0
self._emissive = False
@property
def interpolation(self) -> InterpolationType: # numpydoc ignore=RT01
"""Return or set the default interpolation type.
See :class:`pyvista.plotting.opts.InterpolationType`.
Options are:
* ``'Phong'``
* ``'Flat'``
* ``'Physically based rendering'``
This is stored as an integer value of the ``InterpolationType``
so that the theme can be JSON-serializable.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.interpolation = 'Phong'
>>> pv.global_theme.lighting_params.interpolation
<InterpolationType.PHONG: 2>
"""
return InterpolationType.from_any(self._interpolation)
@interpolation.setter
def interpolation(
self,
interpolation: str | int | InterpolationType,
):
self._interpolation = InterpolationType.from_any(interpolation).value
@property
def metallic(self) -> float: # numpydoc ignore=RT01
"""Return or set the metallic value.
This requires that the interpolation be set to ``'Physically based
rendering'``. Must be between 0 and 1.
Examples
--------
Set the global metallic value used in physically based rendering to
``0.5``.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.metallic = 0.5
>>> pv.global_theme.lighting_params.metallic
0.5
"""
return self._metallic
@metallic.setter
def metallic(self, metallic: float):
_check_range(metallic, (0, 1), 'metallic')
self._metallic = metallic
@property
def roughness(self) -> float: # numpydoc ignore=RT01
"""Return or set the roughness value.
This value has to be between 0 (glossy) and 1 (rough). A glossy
material has reflections and a high specular part. This parameter is
only used by PBR interpolation.
Examples
--------
Set the global roughness value used in physically based rendering to
``0.25``.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.roughness = 0.25
>>> pv.global_theme.lighting_params.roughness
0.25
"""
return self._roughness
@roughness.setter
def roughness(self, roughness: float):
_check_range(roughness, (0, 1), 'roughness')
self._roughness = roughness
@property
def ambient(self) -> float: # numpydoc ignore=RT01
"""Return or set the ambient value.
When lighting is enabled, this is the amount of light in the range of 0
to 1 that reaches the actor when not directed at the light source
emitted from the viewer.
Examples
--------
Set the global ambient lighting value to ``0.2``.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.ambient = 0.2
>>> pv.global_theme.lighting_params.ambient
0.2
"""
return self._ambient
@ambient.setter
def ambient(self, ambient: float):
_check_range(ambient, (0, 1), 'ambient')
self._ambient = ambient
@property
def diffuse(self) -> float: # numpydoc ignore=RT01
"""Return or set the diffuse value.
This is the scattering of light by reflection or
transmission. Diffuse reflection results when light strikes an
irregular surface such as a frosted window or the surface of a
frosted or coated light bulb. Must be between 0 and 1.
Examples
--------
Set the global diffuse lighting value to ``0.5``.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.diffuse = 0.5
>>> pv.global_theme.lighting_params.diffuse
0.5
"""
return self._diffuse
@diffuse.setter
def diffuse(self, diffuse: float):
_check_range(diffuse, (0, 1), 'diffuse')
self._diffuse = diffuse
@property
def specular(self) -> float: # numpydoc ignore=RT01
"""Return or set the specular value.
Specular lighting simulates the bright spot of a light that appears
on shiny objects. Must be between 0 and 1.
Examples
--------
Set the global specular value to ``0.1``.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.specular = 0.1
>>> pv.global_theme.lighting_params.specular
0.1
"""
return self._specular
@specular.setter
def specular(self, specular: float):
_check_range(specular, (0, 1), 'specular')
self._specular = specular
@property
def specular_power(self) -> float: # numpydoc ignore=RT01
"""Return or set the specular power value.
Must be between 0.0 and 128.0.
Examples
--------
Set the global specular power value to ``50``.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.specular_power = 50
>>> pv.global_theme.lighting_params.specular_power
50
"""
return self._specular_power
@specular_power.setter
def specular_power(self, specular_power: float):
_check_range(specular_power, (0, 128), 'specular_power')
self._specular_power = specular_power
@property
def emissive(self) -> bool: # numpydoc ignore=RT01
"""Return or set if emissive is used with point Gaussian style.
Examples
--------
Globally enable emissive lighting when using the point Gaussian style.
>>> import pyvista as pv
>>> pv.global_theme.lighting_params.emissive = True
>>> pv.global_theme.lighting_params.emissive
True
"""
return self._emissive
@emissive.setter
def emissive(self, emissive: bool):
self._emissive = bool(emissive)
class _DepthPeelingConfig(_ThemeConfig):
"""PyVista depth peeling configuration.
Examples
--------
Set global depth peeling parameters.
>>> import pyvista as pv
>>> pv.global_theme.depth_peeling.number_of_peels = 1
>>> pv.global_theme.depth_peeling.occlusion_ratio = 0.0
>>> pv.global_theme.depth_peeling.enabled = True
"""
__slots__ = ['_enabled', '_number_of_peels', '_occlusion_ratio']
def __init__(self):
self._number_of_peels = 4
self._occlusion_ratio = 0.0
self._enabled = False
@property
def number_of_peels(self) -> int: # numpydoc ignore=RT01
"""Return or set the number of peels in depth peeling.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.depth_peeling.number_of_peels = 1
"""
return self._number_of_peels
@number_of_peels.setter
def number_of_peels(self, number_of_peels: int):
self._number_of_peels = int(number_of_peels)
@property
def occlusion_ratio(self) -> float: # numpydoc ignore=RT01
"""Return or set the occlusion ratio in depth peeling.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.depth_peeling.occlusion_ratio = 0.0
"""
return self._occlusion_ratio
@occlusion_ratio.setter
def occlusion_ratio(self, occlusion_ratio: float):
self._occlusion_ratio = float(occlusion_ratio)
@property
def enabled(self) -> bool: # numpydoc ignore=RT01
"""Return or set if depth peeling is enabled.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.depth_peeling.enabled = True
"""
return self._enabled
@enabled.setter
def enabled(self, enabled: bool):
self._enabled = bool(enabled)
def __repr__(self):
txt = ['']
parm = {
'Number': 'number_of_peels',
'Occlusion ratio': 'occlusion_ratio',
'Enabled': 'enabled',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f' {name:<21}: {setting}')
return '\n'.join(txt)
class _SilhouetteConfig(_ThemeConfig):
"""PyVista silhouette configuration.
Examples
--------
Set global silhouette parameters.
>>> import pyvista as pv
>>> pv.global_theme.silhouette.enabled = True
>>> pv.global_theme.silhouette.color = 'grey'
>>> pv.global_theme.silhouette.line_width = 2
>>> pv.global_theme.silhouette.feature_angle = 20
"""
__slots__ = [
'_color',
'_decimate',
'_enabled',
'_feature_angle',
'_line_width',
'_opacity',
]
def __init__(self):
self._color = Color('black')
self._line_width = 2
self._opacity = 1.0
self._feature_angle = None
self._decimate = None
self._enabled = False
@property
def enabled(self) -> bool: # numpydoc ignore=RT01
"""Return or set whether silhouette is on or off."""
return self._enabled
@enabled.setter
def enabled(self, enabled: bool):
self._enabled = bool(enabled)
@property
def color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the silhouette color.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.silhouette.color = 'red'
"""
return self._color
@color.setter
def color(self, color: ColorLike):
self._color = Color(color)
@property
def line_width(self) -> float: # numpydoc ignore=RT01
"""Return or set the silhouette line width.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.silhouette.line_width = 2.0
"""
return self._line_width
@line_width.setter
def line_width(self, line_width: float):
self._line_width = float(line_width) # type: ignore[assignment]
@property
def opacity(self) -> float: # numpydoc ignore=RT01
"""Return or set the silhouette opacity.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.silhouette.opacity = 1.0
"""
return self._opacity
@opacity.setter
def opacity(self, opacity: float):
_check_range(opacity, (0, 1), 'opacity')
self._opacity = float(opacity)
@property
def feature_angle(self) -> float | None: # numpydoc ignore=RT01
"""Return or set the silhouette feature angle.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.silhouette.feature_angle = 20.0
"""
return self._feature_angle
@feature_angle.setter
def feature_angle(self, feature_angle: float | None):
self._feature_angle = feature_angle
@property
def decimate(self) -> float: # numpydoc ignore=RT01
"""Return or set the amount to decimate the silhouette.
Parameter must be between 0 and 1.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.silhouette.decimate = 0.9
"""
return self._decimate # type: ignore[return-value]
@decimate.setter
def decimate(self, decimate: float | None):
if decimate is None:
self._decimate = None
else:
_check_range(decimate, (0, 1), 'decimate')
self._decimate = float(decimate)
def __repr__(self):
txt = ['']
parm = {
'Color': 'color',
'Line width': 'line_width',
'Opacity': 'opacity',
'Feature angle': 'feature_angle',
'Decimate': 'decimate',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f' {name:<21}: {setting}')
return '\n'.join(txt)
class _ColorbarConfig(_ThemeConfig):
"""PyVista colorbar configuration.
Examples
--------
Set the colorbar width.
>>> import pyvista as pv
>>> pv.global_theme.colorbar_horizontal.width = 0.2
"""
__slots__ = ['_height', '_position_x', '_position_y', '_width']
def __init__(self):
self._width = None
self._height = None
self._position_x = None
self._position_y = None
@property
def width(self) -> float: # numpydoc ignore=RT01
"""Return or set colorbar width.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.colorbar_horizontal.width = 0.2
"""
return self._width # type: ignore[return-value]
@width.setter
def width(self, width: float):
self._width = float(width)
@property
def height(self) -> float: # numpydoc ignore=RT01
"""Return or set colorbar height.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.colorbar_horizontal.height = 0.2
"""
return self._height # type: ignore[return-value]
@height.setter
def height(self, height: float):
self._height = float(height)
@property
def position_x(self) -> float: # numpydoc ignore=RT01
"""Return or set colorbar x position.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.colorbar_horizontal.position_x = 0.2
"""
return self._position_x # type: ignore[return-value]
@position_x.setter
def position_x(self, position_x: float):
self._position_x = float(position_x)
@property
def position_y(self) -> float: # numpydoc ignore=RT01
"""Return or set colorbar y position.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.colorbar_horizontal.position_y = 0.2
"""
return self._position_y # type: ignore[return-value]
@position_y.setter
def position_y(self, position_y: float):
self._position_y = float(position_y)
def __repr__(self):
txt = ['']
parm = {
'Width': 'width',
'Height': 'height',
'X Position': 'position_x',
'Y Position': 'position_y',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f' {name:<21}: {setting}')
return '\n'.join(txt)
class _AxesConfig(_ThemeConfig):
"""PyVista axes configuration.
Examples
--------
Show the default axes configuration values.
>>> import pyvista as pv
>>> pv.global_theme.axes.x_color
Color(name='tomato', hex='#ff6347ff', opacity=255)
>>> pv.global_theme.axes.y_color
Color(name='seagreen', hex='#2e8b57ff', opacity=255)
>>> pv.global_theme.axes.z_color
Color(name='blue', hex='#0000ffff', opacity=255)
>>> pv.global_theme.axes.box
False
>>> pv.global_theme.axes.show
True
Set the x-axis color to black.
>>> pv.global_theme.axes.x_color = 'black'
Show the axes orientation widget by default.
>>> pv.global_theme.axes.show = True
Use the :func:`axes orientation box <pyvista.create_axes_orientation_box>`
as the orientation widget.
>>> pv.global_theme.axes.box = True
"""
__slots__ = ['_box', '_show', '_x_color', '_y_color', '_z_color']
def __init__(self):
self._x_color = Color('tomato')
self._y_color = Color('seagreen')
self._z_color = Color('mediumblue')
self._box = False
self._show = True
def __repr__(self):
txt = ['Axes configuration']
parm = {
'X Color': 'x_color',
'Y Color': 'y_color',
'Z Color': 'z_color',
'Use Box': 'box',
'Show': 'show',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f' {name:<21}: {setting}')
return '\n'.join(txt)
@property
def x_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set x-axis color.
Examples
--------
Get the default x-axis color
>>> import pyvista as pv
>>> pv.global_theme.axes.x_color
Color(name='tomato', hex='#ff6347ff', opacity=255)
Change the default color.
>>> pv.global_theme.axes.x_color = 'red'
"""
return self._x_color
@x_color.setter
def x_color(self, color: ColorLike):
self._x_color = Color(color)
@property
def y_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set y-axis color.
Examples
--------
Get the default y-axis color
>>> import pyvista as pv
>>> pv.global_theme.axes.y_color
Color(name='seagreen', hex='#2e8b57ff', opacity=255)
Change the default color.
>>> pv.global_theme.axes.y_color = 'green'
"""
return self._y_color
@y_color.setter
def y_color(self, color: ColorLike):
self._y_color = Color(color)
@property
def z_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set z-axis color.
Examples
--------
Get the default z-axis color
>>> import pyvista as pv
>>> pv.global_theme.axes.z_color
Color(name='blue', hex='#0000ffff', opacity=255)
Change the default color.
>>> pv.global_theme.axes.z_color = 'purple'
"""
return self._z_color
@z_color.setter
def z_color(self, color: ColorLike):
self._z_color = Color(color)
@property
def box(self) -> bool: # numpydoc ignore=RT01
"""Use a box axes orientation widget.
If ``True``, Use the :func:`axes orientation box <pyvista.create_axes_orientation_box>`
instead of the :class:`pyvista.AxesActor` as the orientation widget for plots.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.axes.box = True
"""
return self._box
@box.setter
def box(self, box: bool):
self._box = bool(box)
@property
def show(self) -> bool: # numpydoc ignore=RT01
"""Show or hide the axes actor.
Examples
--------
Hide the axes by default.
>>> import pyvista as pv
>>> pv.global_theme.axes.show = False
"""
return self._show
@show.setter
def show(self, show: bool):
self._show = bool(show)
class _Font(_ThemeConfig):
"""PyVista plotter font configuration.
Examples
--------
Set the default font family to 'arial'. Must be either
'arial', 'courier', or 'times'.
>>> import pyvista as pv
>>> pv.global_theme.font.family = 'arial'
Set the default font size to 20.
>>> pv.global_theme.font.size = 20
Set the default title size to 40
>>> pv.global_theme.font.title_size = 40
Set the default label size to 10
>>> pv.global_theme.font.label_size = 10
Set the default text color to 'grey'
>>> pv.global_theme.font.color = 'grey'
Set the string formatter used to format numerical data to '%.6e'
>>> pv.global_theme.font.fmt = '%.6e'
"""
__slots__ = ['_color', '_family', '_fmt', '_label_size', '_size', '_title_size']
def __init__(self):
self._family = 'arial'
self._size = 12
self._title_size = None
self._label_size = None
self._color = Color('white')
self._fmt = None
def __repr__(self):
txt = ['']
parm = {
'Family': 'family',
'Size': 'size',
'Title size': 'title_size',
'Label size': 'label_size',
'Color': 'color',
'Float format': 'fmt',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f' {name:<21}: {setting}')
return '\n'.join(txt)
@property
def family(self) -> str: # numpydoc ignore=RT01
"""Return or set the font family.
Must be one of the following:
* ``"arial"``
* ``"courier"``
* ``"times"``
Examples
--------
Set the default global font family to 'courier'.
>>> import pyvista as pv
>>> pv.global_theme.font.family = 'courier'
"""
return self._family
@family.setter
def family(self, family: str):
parse_font_family(family) # check valid font
self._family = family
@property
def size(self) -> int: # numpydoc ignore=RT01
"""Return or set the font size.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.font.size = 20
"""
return self._size
@size.setter
def size(self, size: int):
self._size = int(size)
@property
def title_size(self) -> int: # numpydoc ignore=RT01
"""Return or set the title size.
If ``None``, then VTK uses ``UnconstrainedFontSizeOn`` for titles.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.font.title_size = 20
"""
return self._title_size # type: ignore[return-value]
@title_size.setter
def title_size(self, title_size: int | None):
if title_size is None:
self._title_size = None
else:
self._title_size = int(title_size)
@property
def label_size(self) -> int: # numpydoc ignore=RT01
"""Return or set the label size.
If ``None``, then VTK uses ``UnconstrainedFontSizeOn`` for labels.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.font.label_size = 20
"""
return self._label_size # type: ignore[return-value]
@label_size.setter
def label_size(self, label_size: int | None):
if label_size is None:
self._label_size = None
else:
self._label_size = int(label_size)
@property
def color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the font color.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.font.color = 'black'
"""
return self._color
@color.setter
def color(self, color: ColorLike):
self._color = Color(color)
@property
def fmt(self) -> str: # numpydoc ignore=RT01
"""Return or set the string formatter used to format numerical data.
Examples
--------
Set the string formatter used to format numerical data to '%.6e'.
>>> import pyvista as pv
>>> pv.global_theme.font.fmt = '%.6e'
"""
return self._fmt # type: ignore[return-value]
@fmt.setter
def fmt(self, fmt: str):
self._fmt = fmt
class _SliderStyleConfig(_ThemeConfig):
"""PyVista configuration for a single slider style."""
__slots__ = [
'_cap_length',
'_cap_opacity',
'_cap_width',
'_name',
'_slider_color',
'_slider_length',
'_slider_width',
'_tube_color',
'_tube_width',
]
def __init__(self):
"""Initialize the slider style configuration."""
self._name = None
self._slider_length = None
self._slider_width = None
self._slider_color = None
self._tube_width = None
self._tube_color = None
self._cap_opacity = None
self._cap_length = None
self._cap_width = None
@property
def name(self) -> str: # numpydoc ignore=RT01
"""Return the name of the slider style configuration."""
return self._name # type: ignore[return-value]
@name.setter
def name(self, name: str):
self._name = name
@property
def cap_width(self) -> float: # numpydoc ignore=RT01
"""Return or set the cap width.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.cap_width = 0.02
"""
return self._cap_width # type: ignore[return-value]
@cap_width.setter
def cap_width(self, cap_width: float):
self._cap_width = float(cap_width)
@property
def cap_length(self) -> float: # numpydoc ignore=RT01
"""Return or set the cap length.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.cap_length = 0.01
"""
return self._cap_length # type: ignore[return-value]
@cap_length.setter
def cap_length(self, cap_length: float):
self._cap_length = float(cap_length)
@property
def cap_opacity(self) -> float: # numpydoc ignore=RT01
"""Return or set the cap opacity.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.cap_opacity = 1.0
"""
return self._cap_opacity # type: ignore[return-value]
@cap_opacity.setter
def cap_opacity(self, cap_opacity: float):
_check_range(cap_opacity, (0, 1), 'cap_opacity')
self._cap_opacity = float(cap_opacity)
@property
def tube_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the tube color.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.tube_color = 'black'
"""
return self._tube_color # type: ignore[return-value]
@tube_color.setter
def tube_color(self, tube_color: ColorLike):
self._tube_color = Color(tube_color)
@property
def tube_width(self) -> float: # numpydoc ignore=RT01
"""Return or set the tube_width.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.tube_width = 0.005
"""
return self._tube_width # type: ignore[return-value]
@tube_width.setter
def tube_width(self, tube_width: float):
self._tube_width = float(tube_width)
@property
def slider_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the slider color.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.slider_color = 'grey'
"""
return self._slider_color # type: ignore[return-value]
@slider_color.setter
def slider_color(self, slider_color: ColorLike):
self._slider_color = Color(slider_color)
@property
def slider_width(self) -> float: # numpydoc ignore=RT01
"""Return or set the slider width.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.slider_width = 0.04
"""
return self._slider_width # type: ignore[return-value]
@slider_width.setter
def slider_width(self, slider_width: float):
self._slider_width = float(slider_width)
@property
def slider_length(self) -> float: # numpydoc ignore=RT01
"""Return or set the slider_length.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.slider_styles.modern.slider_length = 0.02
"""
return self._slider_length # type: ignore[return-value]
@slider_length.setter
def slider_length(self, slider_length: float):
self._slider_length = float(slider_length)
def __repr__(self):
txt = ['']
parm = {
'Slider length': 'slider_length',
'Slider width': 'slider_width',
'Slider color': 'slider_color',
'Tube width': 'tube_width',
'Tube color': 'tube_color',
'Cap opacity': 'cap_opacity',
'Cap length': 'cap_length',
'Cap width': 'cap_width',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f' {name:<17}: {setting}')
return '\n'.join(txt)
class _SliderConfig(_ThemeConfig):
"""PyVista configuration encompassing all slider styles.
Examples
--------
Set the classic slider configuration.
>>> import pyvista as pv
>>> slider_styles = pv.global_theme.slider_styles
>>> slider_styles.classic.slider_length = 0.02
>>> slider_styles.classic.slider_width = 0.04
>>> slider_styles.classic.slider_color = (0.5, 0.5, 0.5)
>>> slider_styles.classic.tube_width = 0.005
>>> slider_styles.classic.tube_color = (1.0, 1.0, 1.0)
>>> slider_styles.classic.cap_opacity = 1
>>> slider_styles.classic.cap_length = 0.01
>>> slider_styles.classic.cap_width = 0.02
Set the modern slider configuration.
>>> slider_styles.modern.slider_length = 0.02
>>> slider_styles.modern.slider_width = 0.04
>>> slider_styles.modern.slider_color = (0.43, 0.44, 0.45)
>>> slider_styles.modern.tube_width = 0.04
>>> slider_styles.modern.tube_color = (0.69, 0.70, 0.709)
>>> slider_styles.modern.cap_opacity = 0
>>> slider_styles.modern.cap_length = 0.01
>>> slider_styles.modern.cap_width = 0.02
"""
__slots__ = ['_classic', '_modern']
def __init__(self):
"""Initialize the slider configuration."""
self._classic = _SliderStyleConfig()
self._classic.name = 'classic'
self._classic.slider_length = 0.02
self._classic.slider_width = 0.04
self._classic.slider_color = 'gray'
self._classic.tube_width = 0.005
self._classic.tube_color = 'white'
self._classic.cap_opacity = 1
self._classic.cap_length = 0.01
self._classic.cap_width = 0.02
self._modern = _SliderStyleConfig()
self._modern.name = 'modern'
self._modern.slider_length = 0.02
self._modern.slider_width = 0.04
self._modern.slider_color = (110, 113, 117)
self._modern.tube_width = 0.04
self._modern.tube_color = (178, 179, 181)
self._modern.cap_opacity = 0
self._modern.cap_length = 0.01
self._modern.cap_width = 0.02
@property
def classic(self) -> _SliderStyleConfig: # numpydoc ignore=RT01
"""Return the Classic slider configuration."""
return self._classic
@classic.setter
def classic(self, config: _SliderStyleConfig):
if not isinstance(config, _SliderStyleConfig):
msg = 'Configuration type must be `_SliderStyleConfig`' # type: ignore[unreachable]
raise TypeError(msg)
self._classic = config
@property
def modern(self) -> _SliderStyleConfig: # numpydoc ignore=RT01
"""Return the Modern slider configuration."""
return self._modern
@modern.setter
def modern(self, config: _SliderStyleConfig):
if not isinstance(config, _SliderStyleConfig):
msg = 'Configuration type must be `_SliderStyleConfig`' # type: ignore[unreachable]
raise TypeError(msg)
self._modern = config
def __repr__(self):
txt = ['']
parm = {
'Classic': 'classic',
'Modern': 'modern',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f' {name:<21}: {setting}')
return '\n'.join(txt)
def __iter__(self):
for style in [self._classic, self._modern]:
yield style.name
class _TrameConfig(_ThemeConfig):
"""PyVista Trame configuration.
Examples
--------
Set global trame view parameters.
>>> import pyvista as pv
>>> pv.global_theme.trame.interactive_ratio = 2
>>> pv.global_theme.trame.still_ratio = 2
"""
__slots__ = [
'_default_mode',
'_interactive_ratio',
'_jupyter_extension_available',
'_jupyter_extension_enabled',
'_jupyter_server_name',
'_jupyter_server_port',
'_server_proxy_enabled',
'_server_proxy_prefix',
'_still_ratio',
]
def __init__(self):
self._interactive_ratio = 1
self._still_ratio = 1
self._jupyter_server_name = 'pyvista-jupyter'
self._jupyter_server_port = 0
self._server_proxy_enabled = 'PYVISTA_TRAME_SERVER_PROXY_PREFIX' in os.environ
# default for ``jupyter-server-proxy``
service = os.environ.get('JUPYTERHUB_SERVICE_PREFIX', '')
prefix = os.environ.get('PYVISTA_TRAME_SERVER_PROXY_PREFIX', '/proxy/')
if service and not prefix.startswith('http'): # pragma: no cover
self._server_proxy_prefix = str(Path(service) / prefix.lstrip('/')).rstrip('/') + '/'
self._server_proxy_enabled = True
else:
self._server_proxy_prefix = prefix
self._jupyter_extension_available = 'TRAME_JUPYTER_WWW' in os.environ
self._jupyter_extension_enabled = (
self._jupyter_extension_available and not self._server_proxy_enabled
)
# if set, jupyter_mode overwrites defaults
jupyter_mode = os.environ.get('PYVISTA_TRAME_JUPYTER_MODE')
if jupyter_mode == 'extension' and self._jupyter_extension_available: # pragma: no cover
self._server_proxy_enabled = False
self._jupyter_extension_enabled = True
elif jupyter_mode == 'proxy' and self._server_proxy_enabled: # pragma: no cover
self._jupyter_extension_enabled = False
elif jupyter_mode == 'native': # pragma: no cover
self._jupyter_extension_enabled = False
self._server_proxy_enabled = False
self._default_mode = 'trame'
@property
def interactive_ratio(self) -> float: # numpydoc ignore=RT01
"""Return or set the interactive ratio for PyVista Trame views.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.trame.interactive_ratio = 2
"""
return self._interactive_ratio
@interactive_ratio.setter
def interactive_ratio(self, interactive_ratio: float):
self._interactive_ratio = interactive_ratio # type: ignore[assignment]
@property
def still_ratio(self) -> float: # numpydoc ignore=RT01
"""Return or set the still ratio for PyVista Trame views.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.trame.still_ratio = 2
"""
return self._still_ratio
@still_ratio.setter
def still_ratio(self, still_ratio: float):
self._still_ratio = still_ratio # type: ignore[assignment]
@property
def jupyter_server_name(self): # numpydoc ignore=RT01
"""Return or set the trame server name PyVista uses in Jupyter.
This defaults to ``'pyvista-jupyter'``.
This must be set before running :func:`pyvista.set_jupyter_backend`
to ensure a server of this name is launched.
Most users should not need to modify this.
"""
return self._jupyter_server_name
@jupyter_server_name.setter
def jupyter_server_name(self, name: str):
self._jupyter_server_name = name
@property
def jupyter_server_port(self) -> int: # numpydoc ignore=RT01
"""Return or set the port for the Trame Jupyter server."""
return self._jupyter_server_port
@jupyter_server_port.setter
def jupyter_server_port(self, port: int):
self._jupyter_server_port = port
@property
def server_proxy_enabled(self) -> bool: # numpydoc ignore=RT01
"""Return or set if use of relative URLs is enabled for the Jupyter interface."""
return self._server_proxy_enabled
@server_proxy_enabled.setter
def server_proxy_enabled(self, enabled: bool):
if enabled and self.jupyter_extension_enabled:
warnings.warn('Enabling server_proxy will disable jupyter_extension')
self._jupyter_extension_enabled = False
self._server_proxy_enabled = bool(enabled)
@property
def server_proxy_prefix(self): # numpydoc ignore=RT01
"""Return or set URL prefix when using relative URLs with the Jupyter interface."""
return self._server_proxy_prefix
@server_proxy_prefix.setter
def server_proxy_prefix(self, prefix: str):
self._server_proxy_prefix = prefix
@property
def jupyter_extension_available(self) -> bool: # numpydoc ignore=RT01
"""Return whether the trame_jupyter_extension is detected."""
return self._jupyter_extension_available
@jupyter_extension_available.setter
def jupyter_extension_available(self, _available: bool):
warnings.warn(
'The jupyter_extension_available flag is read only and is automatically detected.',
)
@property
def jupyter_extension_enabled(self) -> bool: # numpydoc ignore=RT01
"""Return or set whether to use the trame_jupyter_extension to communicate with clients."""
return self._jupyter_extension_enabled
@jupyter_extension_enabled.setter
def jupyter_extension_enabled(self, enabled: bool):
if enabled and not self.jupyter_extension_available:
msg = 'The trame_jupyter_extension is not available'
raise ValueError(msg)
if enabled and self.server_proxy_enabled:
warnings.warn('Enabling jupyter_extension will disable server_proxy')
self._server_proxy_enabled = False
self._jupyter_extension_enabled = bool(enabled)
@property
def default_mode(self): # numpydoc ignore=RT01
"""Return or set the default mode of the Trame backend.
* ``'trame'``: Uses a view that can switch between client and server
rendering modes.
* ``'server'``: Uses a view that is purely server rendering.
* ``'client'``: Uses a view that is purely client rendering (generally
safe without a virtual frame buffer)
"""
return self._default_mode
@default_mode.setter
def default_mode(self, mode: str):
self._default_mode = mode
class _CameraConfig(_ThemeConfig):
"""PyVista camera configuration.
Examples
--------
Set global camera parameters.
>>> import pyvista as pv
>>> pv.global_theme.camera.position = [1.0, 1.0, 1.0]
>>> pv.global_theme.camera.viewup = [0.0, 0.0, 1.0]
"""
__slots__ = [
'_parallel_projection',
'_parallel_scale',
'_position',
'_viewup',
]
def __init__(self):
self._position = [1.0, 1.0, 1.0]
self._viewup = [0.0, 0.0, 1.0]
self._parallel_projection = False
self._parallel_scale = 1.0
@property
def position(self) -> VectorLike[float]: # numpydoc ignore=RT01
"""Return or set the camera position.
Examples
--------
Set camera position.
>>> import pyvista as pv
>>> pv.global_theme.camera.position = [1.0, 1.0, 1.0]
"""
return self._position
@position.setter
def position(self, position: VectorLike[float]):
self._position = position # type: ignore[assignment]
@property
def viewup(self) -> VectorLike[float]: # numpydoc ignore=RT01
"""Return or set the camera viewup.
Examples
--------
Set camera viewup.
>>> import pyvista as pv
>>> pv.global_theme.camera.viewup = [0.0, 0.0, 1.0]
"""
return self._viewup
@viewup.setter
def viewup(self, viewup: VectorLike[float]):
self._viewup = viewup # type: ignore[assignment]
@property
def parallel_projection(self) -> bool: # numpydoc ignore=RT01
"""Return or set parallel projection mode.
Examples
--------
Enable parallel projection.
>>> import pyvista as pv
>>> pv.global_theme.camera.parallel_projection = True
"""
return self._parallel_projection
@parallel_projection.setter
def parallel_projection(self, value: bool) -> None:
self._parallel_projection = value
@property
def parallel_scale(self) -> bool: # numpydoc ignore=RT01
"""Return or set parallel scale.
Examples
--------
Set parallel scale.
>>> import pyvista as pv
>>> pv.global_theme.camera.parallel_scale = 2.0
"""
return self._parallel_scale # type: ignore[return-value]
@parallel_scale.setter
def parallel_scale(self, value: bool) -> None:
self._parallel_scale = value
class Theme(_ThemeConfig):
"""Base VTK theme.
Examples
--------
Change the global default background color to white.
>>> import pyvista as pv
>>> pv.global_theme.color = 'white'
Show edges by default.
>>> pv.global_theme.show_edges = True
Create a new theme from the default theme and apply it globally.
>>> from pyvista.plotting.themes import DocumentTheme
>>> my_theme = DocumentTheme()
>>> my_theme.color = 'red'
>>> my_theme.background = 'white'
>>> pv.global_theme.load_theme(my_theme)
"""
__slots__ = [
'_above_range_color',
'_allow_empty_mesh',
'_anti_aliasing',
'_auto_close',
'_axes',
'_background',
'_before_close_callback',
'_before_close_callback',
'_below_range_color',
'_camera',
'_cmap',
'_color',
'_color_cycler',
'_colorbar_horizontal',
'_colorbar_orientation',
'_colorbar_vertical',
'_depth_peeling',
'_edge_color',
'_edge_opacity',
'_enable_camera_orientation_widget',
'_floor_color',
'_font',
'_full_screen',
'_hidden_line_removal',
'_image_scale',
'_interactive',
'_interpolate_before_map',
'_jupyter_backend',
'_lighting',
'_lighting_params',
'_line_width',
'_logo_file',
'_multi_rendering_splitting_position',
'_multi_samples',
'_name',
'_nan_color',
'_notebook',
'_opacity',
'_outline_color',
'_point_size',
'_render_lines_as_tubes',
'_render_points_as_spheres',
'_resample_environment_texture',
'_return_cpos',
'_sharp_edges_feature_angle',
'_show_edges',
'_show_scalar_bar',
'_show_vertices',
'_silhouette',
'_slider_styles',
'_smooth_shading',
'_split_sharp_edges',
'_title',
'_trame',
'_transparent_background',
'_volume_mapper',
'_window_size',
]
def __init__(self):
"""Initialize the theme."""
self._name = 'default'
self._background = Color([0.3, 0.3, 0.3])
self._full_screen = False
self._camera = _CameraConfig()
self._notebook = None
self._window_size = [1024, 768]
self._image_scale = 1
self._font = _Font()
self._cmap: ColormapOptions = 'viridis'
self._color = Color('white')
self._color_cycler = None
self._nan_color = Color('darkgray')
self._above_range_color = Color('grey')
self._below_range_color = Color('grey')
self._edge_color = Color('black')
self._line_width = 1.0
self._point_size = 5.0
self._outline_color = Color('white')
self._floor_color = Color('gray')
self._colorbar_orientation = 'horizontal'
self._colorbar_horizontal = _ColorbarConfig()
self._colorbar_horizontal.width = 0.6
self._colorbar_horizontal.height = 0.08
self._colorbar_horizontal.position_x = 0.35
self._colorbar_horizontal.position_y = 0.05
self._colorbar_vertical = _ColorbarConfig()
self._colorbar_vertical.width = 0.08
self._colorbar_vertical.height = 0.45
self._colorbar_vertical.position_x = 0.9
self._colorbar_vertical.position_y = 0.02
self._show_scalar_bar = True
self._show_edges = False
self._show_vertices = False
self._lighting = True
self._interactive = False
self._render_points_as_spheres = False
self._render_lines_as_tubes = False
self._transparent_background = False
self._title = 'PyVista'
self._axes = _AxesConfig()
self._split_sharp_edges = False
self._sharp_edges_feature_angle = 30.0
self._before_close_callback = None
self._allow_empty_mesh = False
# Grab system flag for anti-aliasing
# Use a default value of 8 multi-samples as this is default for VTK
try:
self._multi_samples = int(os.environ.get('PYVISTA_MULTI_SAMPLES', '8'))
except ValueError: # pragma: no cover
self._multi_samples = 8
# Grab system flag for auto-closing
self._auto_close = os.environ.get('PYVISTA_AUTO_CLOSE', '').lower() != 'false'
self._jupyter_backend: JupyterBackendOptions = (
os.environ.get('PYVISTA_JUPYTER_BACKEND', 'trame') # type: ignore[assignment]
)
self._trame = _TrameConfig()
self._multi_rendering_splitting_position = None
self._volume_mapper = 'smart'
self._smooth_shading = False
self._depth_peeling = _DepthPeelingConfig()
self._silhouette = _SilhouetteConfig()
self._slider_styles = _SliderConfig()
self._return_cpos = True
self._hidden_line_removal = False
self._anti_aliasing = 'msaa'
self._enable_camera_orientation_widget = False
self._lighting_params = _LightingConfig()
self._interpolate_before_map = True
self._opacity = 1.0
self._edge_opacity = 1.0
self._logo_file = None
self._resample_environment_texture: bool | float = False
@property
def hidden_line_removal(self) -> bool: # numpydoc ignore=RT01
"""Return or set hidden line removal.
Wireframe geometry will be drawn using hidden line removal if
the rendering engine supports it.
See Also
--------
pyvista.Plotter.enable_hidden_line_removal
Examples
--------
Enable hidden line removal.
>>> import pyvista as pv
>>> pv.global_theme.hidden_line_removal = True
>>> pv.global_theme.hidden_line_removal
True
"""
return self._hidden_line_removal
@hidden_line_removal.setter
def hidden_line_removal(self, value: bool):
self._hidden_line_removal = value
@property
def interpolate_before_map(self) -> bool: # numpydoc ignore=RT01
"""Return or set whether to interpolate colors before mapping.
If the ``interpolate_before_map`` is turned off, the color
mapping occurs at polygon points and colors are interpolated,
which is generally less accurate whereas if the
``interpolate_before_map`` is on (the default), then the scalars
will be interpolated across the topology of the dataset which is
more accurate.
See also :ref:`interpolate_before_map_example`.
Examples
--------
Enable hidden line removal.
>>> import pyvista as pv
Load a cylinder which has cells with a wide spread
>>> cyl = pv.Cylinder(direction=(0, 0, 1), height=2).elevation()
Common display argument to make sure all else is constant
>>> dargs = dict(scalars='Elevation', cmap='rainbow', show_edges=True)
>>> p = pv.Plotter(shape=(1, 2))
>>> _ = p.add_mesh(
... cyl,
... interpolate_before_map=False,
... scalar_bar_args={'title': 'Elevation - interpolated'},
... **dargs,
... )
>>> p.subplot(0, 1)
>>> _ = p.add_mesh(
... cyl,
... interpolate_before_map=True,
... scalar_bar_args={'title': 'Elevation - interpolated'},
... **dargs,
... )
>>> p.link_views()
>>> p.camera_position = [
... (-1.67, -5.10, 2.06),
... (0.0, 0.0, 0.0),
... (0.00, 0.37, 0.93),
... ]
>>> p.show() # doctest: +SKIP
"""
return self._interpolate_before_map
@interpolate_before_map.setter
def interpolate_before_map(self, value: bool):
self._interpolate_before_map = value
@property
def opacity(self) -> float: # numpydoc ignore=RT01
"""Return or set the opacity.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.opacity = 0.5
"""
return self._opacity
@opacity.setter
def opacity(self, opacity: float):
_check_range(opacity, (0, 1), 'opacity')
self._opacity = float(opacity)
@property
def edge_opacity(self) -> float: # numpydoc ignore=RT01
"""Return or set the edges opacity.
.. note::
`edge_opacity` uses ``SetEdgeOpacity`` as the underlying method which
requires VTK version 9.3 or higher. If ``SetEdgeOpacity`` is not
available, `edge_opacity` is set to 1.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.edge_opacity = 0.5
"""
return self._edge_opacity
@edge_opacity.setter
def edge_opacity(self, edge_opacity: float):
_check_range(edge_opacity, (0, 1), 'edge_opacity')
self._edge_opacity = float(edge_opacity)
@property
def above_range_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default above range color.
Examples
--------
Set the above range color to red.
>>> import pyvista as pv
>>> pv.global_theme.above_range_color = 'r'
>>> pv.global_theme.above_range_color
Color(name='red', hex='#ff0000ff', opacity=255)
"""
return self._above_range_color
@above_range_color.setter
def above_range_color(self, value: ColorLike):
self._above_range_color = Color(value)
@property
def below_range_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default below range color.
Examples
--------
Set the below range color to blue.
>>> import pyvista as pv
>>> pv.global_theme.below_range_color = 'b'
>>> pv.global_theme.below_range_color
Color(name='blue', hex='#0000ffff', opacity=255)
"""
return self._below_range_color
@below_range_color.setter
def below_range_color(self, value: ColorLike):
self._below_range_color = Color(value)
@property
def return_cpos(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default behavior of returning the camera position.
Examples
--------
Disable returning camera position by ``show`` and ``plot`` methods.
>>> import pyvista as pv
>>> pv.global_theme.return_cpos = False
"""
return self._return_cpos
@return_cpos.setter
def return_cpos(self, value: bool):
self._return_cpos = value
@property
def background(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default background color of pyvista plots.
Examples
--------
Set the default global background of all plots to white.
>>> import pyvista as pv
>>> pv.global_theme.background = 'white'
"""
return self._background
@background.setter
def background(self, new_background: ColorLike) -> None:
self._background = Color(new_background)
@property
def jupyter_backend(
self,
) -> JupyterBackendOptions: # numpydoc ignore=RT01
"""Return or set the jupyter notebook plotting backend.
Jupyter backend to use when plotting. Must be one of the
following:
* ``'static'`` : Display a single static image within the
Jupyterlab environment. Still requires that a virtual
framebuffer be set up when displaying on a headless server,
but does not require any additional modules to be installed.
* ``'client'`` : Export/serialize the scene graph to be rendered
with VTK.js client-side through ``trame``. Requires ``trame``
and ``jupyter-server-proxy`` to be installed.
* ``'server'``: Render remotely and stream the resulting VTK
images back to the client using ``trame``. This replaces the
``'ipyvtklink'`` backend with better performance.
Supports the most VTK features, but suffers from minor lag due
to remote rendering. Requires that a virtual framebuffer be set
up when displaying on a headless server. Must have at least ``trame``
and ``jupyter-server-proxy`` installed for cloud/remote Jupyter
instances. This mode is also aliased by ``'trame'``.
* ``'trame'``: The full Trame-based backend that combines both
``'server'`` and ``'client'`` into one backend. This requires a
virtual frame buffer.
* ``'html'``: The ``'client'`` backend, but able to be embedded.
* ``'none'`` : Do not display any plots within jupyterlab,
instead display using dedicated VTK render windows. This
will generate nothing on headless servers even with a
virtual framebuffer.
Examples
--------
Just show static images.
>>> pv.set_jupyter_backend('static') # doctest:+SKIP
Disable all plotting within JupyterLab and display using a
standard desktop VTK render window.
>>> pv.set_jupyter_backend(None) # doctest:+SKIP
"""
return self._jupyter_backend
@jupyter_backend.setter
def jupyter_backend(self, backend: str):
from pyvista.jupyter import _validate_jupyter_backend # noqa: PLC0415
self._jupyter_backend = _validate_jupyter_backend(backend)
@property
def trame(self) -> _TrameConfig: # numpydoc ignore=RT01
"""Return or set the default trame parameters."""
return self._trame
@trame.setter
def trame(self, config: _TrameConfig):
if not isinstance(config, _TrameConfig):
msg = 'Configuration type must be `_TrameConfig`.' # type: ignore[unreachable]
raise TypeError(msg)
self._trame = config
@property
def auto_close(self) -> bool: # numpydoc ignore=RT01
"""Automatically close the figures when finished plotting.
.. DANGER::
Set to ``False`` with extreme caution.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.auto_close = False
"""
return self._auto_close
@auto_close.setter
def auto_close(self, value: bool):
self._auto_close = value
@property
def full_screen(self) -> bool: # numpydoc ignore=RT01
"""Return if figures are shown in full screen.
Examples
--------
Set windows to be full screen by default.
>>> import pyvista as pv
>>> pv.global_theme.full_screen = True
"""
return self._full_screen
@full_screen.setter
def full_screen(self, value: bool):
self._full_screen = value
@property
def enable_camera_orientation_widget(self) -> bool: # numpydoc ignore=RT01
"""Enable the camera orientation widget in all plotters.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.enable_camera_orientation_widget = True
>>> pv.global_theme.enable_camera_orientation_widget
True
"""
return self._enable_camera_orientation_widget
@enable_camera_orientation_widget.setter
def enable_camera_orientation_widget(self, value: bool):
self._enable_camera_orientation_widget = value
@property
def camera(self): # numpydoc ignore=RT01
"""Return or set the default camera position.
Examples
--------
Set both the position and viewup of the camera.
>>> import pyvista as pv
>>> pv.global_theme.camera.position = [1.0, 1.0, 1.0]
>>> pv.global_theme.camera.viewup = [0.0, 0.0, 1.0]
"""
return self._camera
@camera.setter
def camera(self, camera):
if isinstance(camera, dict):
self._camera = _CameraConfig.from_dict(camera)
elif isinstance(camera, _CameraConfig):
self._camera = camera
else:
msg = f'camera value must either be a `dict` or a `_CameraConfig`, got {type(camera)}'
raise TypeError(msg)
@property
def notebook(self) -> bool | None: # numpydoc ignore=RT01
"""Return or set the state of notebook plotting.
Setting this to ``True`` always enables notebook plotting,
while setting it to ``False`` disables plotting even when
plotting within a jupyter notebook and plots externally.
Examples
--------
Disable all jupyter notebook plotting.
>>> import pyvista as pv
>>> pv.global_theme.notebook = False
"""
return self._notebook
@notebook.setter
def notebook(self, value: bool | None):
self._notebook = value
@property
def window_size(self) -> list[int]: # numpydoc ignore=RT01
"""Return or set the default render window size.
Examples
--------
Set window size to ``[400, 400]``.
>>> import pyvista as pv
>>> pv.global_theme.window_size = [400, 400]
"""
return self._window_size
@window_size.setter
def window_size(self, window_size: list[int]):
if len(window_size) != 2:
msg = 'Expected a length 2 iterable for ``window_size``.'
raise ValueError(msg)
# ensure positive size
if window_size[0] < 0 or window_size[1] < 0:
msg = 'Window size must be a positive value.'
raise ValueError(msg)
self._window_size = window_size
@property
def image_scale(self) -> int: # numpydoc ignore=RT01
"""Return or set the default image scale factor."""
return self._image_scale
@image_scale.setter
def image_scale(self, value: int):
value = int(value)
if value < 1:
msg = 'Scale factor must be a positive integer.'
raise ValueError(msg)
self._image_scale = int(value)
@property
def font(self) -> _Font: # numpydoc ignore=RT01
"""Return or set the default font size, family, and/or color.
Examples
--------
Set the default font family to 'arial'. Must be either
'arial', 'courier', or 'times'.
>>> import pyvista as pv
>>> pv.global_theme.font.family = 'arial'
Set the default font size to 20.
>>> pv.global_theme.font.size = 20
Set the default title size to 40.
>>> pv.global_theme.font.title_size = 40
Set the default label size to 10.
>>> pv.global_theme.font.label_size = 10
Set the default text color to 'grey'.
>>> pv.global_theme.font.color = 'grey'
String formatter used to format numerical data to '%.6e'.
>>> pv.global_theme.font.fmt = '%.6e'
"""
return self._font
@font.setter
def font(self, config: _Font):
if not isinstance(config, _Font):
msg = 'Configuration type must be `_Font`.' # type: ignore[unreachable]
raise TypeError(msg)
self._font = config
@property
def cmap(self) -> ColormapOptions: # numpydoc ignore=RT01
"""Return or set the default colormap of pyvista.
See :ref:`named_colormaps` for supported colormaps.
Only applicable when displaying ``scalars``.
You can also specify a list of colors to override an existing
colormap with a custom one. For example, to create a three
color colormap you might specify ``['green', 'red', 'blue']``
Examples
--------
Set the default global colormap to 'jet'.
>>> import pyvista as pv
>>> pv.global_theme.cmap = 'jet'
"""
return self._cmap
@cmap.setter
def cmap(self, cmap: ColormapOptions):
get_cmap_safe(cmap) # for validation
self._cmap = cmap
@property
def color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default color of meshes in pyvista.
Used for meshes without ``scalars``.
When setting, the value must be either a string, rgb list,
or hex color string. For example:
* ``color='white'``
* ``color='w'``
* ``color=[1.0, 1.0, 1.0]``
* ``color='#FFFFFF'``
Examples
--------
Set the default mesh color to 'red'.
>>> import pyvista as pv
>>> pv.global_theme.color = 'red'
"""
return self._color
@color.setter
def color(self, color: ColorLike):
self._color = Color(color)
@property
def color_cycler(self): # numpydoc ignore=RT01
"""Return or set the default color cycler used to color meshes.
This color cycler is iterated over by each renderer to sequentially
color datasets when displaying them through ``add_mesh``.
When setting, the value must be either a list of color-like objects,
or a cycler of color-like objects. If the value passed is a single
string, it must be one of:
* ``'default'`` - Use the default color cycler (matches matplotlib's default)
* ``'matplotlib`` - Dynamically get matplotlib's current theme's color cycler.
* ``'all'`` - Cycle through all available colors in
``pyvista.plotting.colors.hexcolors``
Setting to ``None`` will disable the use of the color cycler.
Examples
--------
Set the default color cycler to iterate through red, green, and blue.
>>> import pyvista as pv
>>> pv.global_theme.color_cycler = ['red', 'green', 'blue']
>>> pl = pv.Plotter()
>>> _ = pl.add_mesh(pv.Cone(center=(0, 0, 0))) # red
>>> _ = pl.add_mesh(pv.Cube(center=(1, 0, 0))) # green
>>> _ = pl.add_mesh(pv.Sphere(center=(1, 1, 0))) # blue
>>> _ = pl.add_mesh(pv.Cylinder(center=(0, 1, 0))) # red again
>>> pl.show() # doctest: +SKIP
"""
return self._color_cycler
@color_cycler.setter
def color_cycler(self, color_cycler):
self._color_cycler = get_cycler(color_cycler)
@property
def nan_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default NaN color.
This color is used to plot all NaN values.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.nan_color = 'darkgray'
"""
return self._nan_color
@nan_color.setter
def nan_color(self, nan_color: ColorLike):
self._nan_color = Color(nan_color)
@property
def edge_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default edge color.
Examples
--------
Set the global edge color to 'blue'.
>>> import pyvista as pv
>>> pv.global_theme.edge_color = 'blue'
"""
return self._edge_color
@edge_color.setter
def edge_color(self, edge_color: ColorLike):
self._edge_color = Color(edge_color)
@property
def line_width(self) -> float: # numpydoc ignore=RT01
"""Return or set the default line width.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.line_width = 2.0
"""
return self._line_width
@line_width.setter
def line_width(self, line_width: float):
self._line_width = float(line_width)
@property
def point_size(self) -> float: # numpydoc ignore=RT01
"""Return or set the default point size.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.line_width = 10.0
"""
return self._point_size
@point_size.setter
def point_size(self, point_size: float):
self._point_size = float(point_size)
@property
def outline_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default outline color.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.outline_color = 'white'
"""
return self._outline_color
@outline_color.setter
def outline_color(self, outline_color: ColorLike):
self._outline_color = Color(outline_color)
@property
def floor_color(self) -> Color: # numpydoc ignore=RT01
"""Return or set the default floor color.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.floor_color = 'black'
"""
return self._floor_color
@floor_color.setter
def floor_color(self, floor_color: ColorLike):
self._floor_color = Color(floor_color)
@property
def colorbar_orientation(self) -> str: # numpydoc ignore=RT01
"""Return or set the default colorbar orientation.
Must be either ``'vertical'`` or ``'horizontal'``.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.colorbar_orientation = 'horizontal'
"""
return self._colorbar_orientation
@colorbar_orientation.setter
def colorbar_orientation(self, colorbar_orientation: str):
if colorbar_orientation not in ['vertical', 'horizontal']:
msg = 'Colorbar orientation must be either "vertical" or "horizontal"'
raise ValueError(msg)
self._colorbar_orientation = colorbar_orientation
@property
def colorbar_horizontal(self) -> _ColorbarConfig: # numpydoc ignore=RT01
"""Return or set the default parameters of a horizontal colorbar.
Examples
--------
Set the default horizontal colorbar width to 0.6.
>>> import pyvista as pv
>>> pv.global_theme.colorbar_horizontal.width = 0.6
Set the default horizontal colorbar height to 0.2.
>>> pv.global_theme.colorbar_horizontal.height = 0.2
"""
return self._colorbar_horizontal
@colorbar_horizontal.setter
def colorbar_horizontal(self, config: _ColorbarConfig):
if not isinstance(config, _ColorbarConfig):
msg = 'Configuration type must be `_ColorbarConfig`.' # type: ignore[unreachable]
raise TypeError(msg)
self._colorbar_horizontal = config
@property
def colorbar_vertical(self) -> _ColorbarConfig: # numpydoc ignore=RT01
"""Return or set the default parameters of a vertical colorbar.
Examples
--------
Set the default colorbar width to 0.45.
>>> import pyvista as pv
>>> pv.global_theme.colorbar_vertical.width = 0.45
Set the default colorbar height to 0.8.
>>> import pyvista as pv
>>> pv.global_theme.colorbar_vertical.height = 0.8
"""
return self._colorbar_vertical
@colorbar_vertical.setter
def colorbar_vertical(self, config: _ColorbarConfig):
if not isinstance(config, _ColorbarConfig):
msg = 'Configuration type must be `_ColorbarConfig`.' # type: ignore[unreachable]
raise TypeError(msg)
self._colorbar_vertical = config
@property
def show_scalar_bar(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default color bar visibility.
Examples
--------
Show the scalar bar by default when scalars are available.
>>> import pyvista as pv
>>> pv.global_theme.show_scalar_bar = True
"""
return self._show_scalar_bar
@show_scalar_bar.setter
def show_scalar_bar(self, show_scalar_bar: bool):
self._show_scalar_bar = bool(show_scalar_bar)
@property
def show_edges(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default edge visibility.
Examples
--------
Show edges globally by default.
>>> import pyvista as pv
>>> pv.global_theme.show_edges = True
"""
return self._show_edges
@show_edges.setter
def show_edges(self, show_edges: bool):
self._show_edges = bool(show_edges)
@property
def show_vertices(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default vertex visibility.
Examples
--------
Show vertices globally by default.
>>> import pyvista as pv
>>> pv.global_theme.show_vertices = True
"""
return self._show_vertices
@show_vertices.setter
def show_vertices(self, show_vertices: bool):
self._show_vertices = bool(show_vertices)
@property
def lighting(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default ``lighting``.
Examples
--------
Disable lighting globally.
>>> import pyvista as pv
>>> pv.global_theme.lighting = False
"""
return self._lighting
@lighting.setter
def lighting(self, lighting: bool):
self._lighting = lighting
@property
def interactive(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default ``interactive`` parameter.
Examples
--------
Make all plots non-interactive globally.
>>> import pyvista as pv
>>> pv.global_theme.interactive = False
"""
return self._interactive
@interactive.setter
def interactive(self, interactive: bool):
self._interactive = bool(interactive)
@property
def render_points_as_spheres(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default ``render_points_as_spheres`` parameter.
Examples
--------
Render points as spheres by default globally.
>>> import pyvista as pv
>>> pv.global_theme.render_points_as_spheres = True
"""
return self._render_points_as_spheres
@render_points_as_spheres.setter
def render_points_as_spheres(self, render_points_as_spheres: bool):
self._render_points_as_spheres = bool(render_points_as_spheres)
@property
def render_lines_as_tubes(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default ``render_lines_as_tubes`` parameter.
Examples
--------
Render points as spheres by default globally.
>>> import pyvista as pv
>>> pv.global_theme.render_lines_as_tubes = True
"""
return self._render_lines_as_tubes
@render_lines_as_tubes.setter
def render_lines_as_tubes(self, render_lines_as_tubes: bool):
self._render_lines_as_tubes = bool(render_lines_as_tubes)
@property
def transparent_background(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default ``transparent_background`` parameter.
Examples
--------
Set transparent_background globally to ``True``.
>>> import pyvista as pv
>>> pv.global_theme.transparent_background = True
"""
return self._transparent_background
@transparent_background.setter
def transparent_background(self, transparent_background: bool):
self._transparent_background = transparent_background
@property
def title(self) -> str: # numpydoc ignore=RT01
"""Return or set the default ``title`` parameter.
This is the VTK render window title.
Examples
--------
Set title globally to 'plot'.
>>> import pyvista as pv
>>> pv.global_theme.title = 'plot'
"""
return self._title
@title.setter
def title(self, title: str):
self._title = title
@property
def anti_aliasing(self) -> str | None: # numpydoc ignore=RT01
"""Enable or disable anti-aliasing.
Should be either ``"ssaa"``, ``"msaa"``, ``"fxaa"``, or ``None``.
Examples
--------
Use super-sampling anti-aliasing in the global theme.
>>> import pyvista as pv
>>> pv.global_theme.anti_aliasing = 'ssaa'
>>> pv.global_theme.anti_aliasing
'ssaa'
Disable anti-aliasing in the global theme.
>>> import pyvista as pv
>>> pv.global_theme.anti_aliasing = None
See :ref:`anti_aliasing_example` for more information regarding
anti-aliasing.
"""
return self._anti_aliasing
@anti_aliasing.setter
def anti_aliasing(self, anti_aliasing: str | None):
if isinstance(anti_aliasing, str):
if anti_aliasing not in ['ssaa', 'msaa', 'fxaa']:
msg = 'anti_aliasing must be either "ssaa", "msaa", or "fxaa"'
raise ValueError(msg)
elif anti_aliasing is not None:
msg = 'anti_aliasing must be either "ssaa", "msaa", "fxaa", or None' # type: ignore[unreachable]
raise TypeError(msg)
self._anti_aliasing = anti_aliasing # type: ignore[assignment]
@property
def multi_samples(self) -> int: # numpydoc ignore=RT01
"""Return or set the default ``multi_samples`` parameter.
Set the number of multisamples to used with hardware anti_aliasing. This
is only used when :attr:`anti_aliasing <Theme.anti_aliasing>` is
set to ``"msaa"``.
Examples
--------
Set the default number of multisamples to 2 and enable ``"msaa"``
>>> import pyvista as pv
>>> pv.global_theme.anti_aliasing = 'msaa'
>>> pv.global_theme.multi_samples = 2
"""
return self._multi_samples
@multi_samples.setter
def multi_samples(self, multi_samples: int):
self._multi_samples = int(multi_samples)
@property
def multi_rendering_splitting_position(self) -> float: # numpydoc ignore=RT01
"""Return or set the default ``multi_rendering_splitting_position`` parameter.
Examples
--------
Set multi_rendering_splitting_position globally to 0.5 (the
middle of the window).
>>> import pyvista as pv
>>> pv.global_theme.multi_rendering_splitting_position = 0.5
"""
return self._multi_rendering_splitting_position # type: ignore[return-value]
@multi_rendering_splitting_position.setter
def multi_rendering_splitting_position(
self,
multi_rendering_splitting_position: float,
):
self._multi_rendering_splitting_position = multi_rendering_splitting_position
@property
def volume_mapper(self) -> str: # numpydoc ignore=RT01
"""Return or set the default ``volume_mapper`` parameter.
Must be one of the following strings, which are mapped to the
following VTK volume mappers.
* ``'fixed_point'`` : :vtk:`vtkFixedPointVolumeRayCastMapper`
* ``'gpu'`` : :vtk:`vtkGPUVolumeRayCastMapper`
* ``'open_gl'`` : :vtk:`vtkOpenGLGPUVolumeRayCastMapper`
* ``'smart'`` : :vtk:`vtkSmartVolumeMapper`
Examples
--------
Set default volume mapper globally to 'gpu'.
>>> import pyvista as pv
>>> pv.global_theme.volume_mapper = 'gpu'
"""
return self._volume_mapper
@volume_mapper.setter
def volume_mapper(self, mapper: str):
mappers = ['fixed_point', 'gpu', 'open_gl', 'smart']
if mapper not in mappers:
msg = (
f'Mapper ({mapper}) unknown. Available volume mappers '
f'include:\n {", ".join(mappers)}'
)
raise ValueError(msg)
self._volume_mapper = mapper
@property
def smooth_shading(self) -> bool: # numpydoc ignore=RT01
"""Return or set the default ``smooth_shading`` parameter.
Examples
--------
Set the global smooth_shading parameter default to ``True``.
>>> import pyvista as pv
>>> pv.global_theme.smooth_shading = True
"""
return self._smooth_shading
@smooth_shading.setter
def smooth_shading(self, smooth_shading: bool):
self._smooth_shading = bool(smooth_shading)
@property
def depth_peeling(self) -> _DepthPeelingConfig: # numpydoc ignore=RT01
"""Return or set the default depth peeling parameters.
Examples
--------
Set the global depth_peeling parameter default to be enabled
with 8 peels.
>>> import pyvista as pv
>>> pv.global_theme.depth_peeling.number_of_peels = 8
>>> pv.global_theme.depth_peeling.occlusion_ratio = 0.0
>>> pv.global_theme.depth_peeling.enabled = True
"""
return self._depth_peeling
@depth_peeling.setter
def depth_peeling(self, config: _DepthPeelingConfig):
if not isinstance(config, _DepthPeelingConfig):
msg = 'Configuration type must be `_DepthPeelingConfig`.' # type: ignore[unreachable]
raise TypeError(msg)
self._depth_peeling = config
@property
def silhouette(self) -> _SilhouetteConfig: # numpydoc ignore=RT01
"""Return or set the default ``silhouette`` configuration.
Examples
--------
Set parameters of the silhouette.
>>> import pyvista as pv
>>> pv.global_theme.silhouette.color = 'grey'
>>> pv.global_theme.silhouette.line_width = 2.0
>>> pv.global_theme.silhouette.feature_angle = 20
"""
return self._silhouette
@silhouette.setter
def silhouette(self, config: _SilhouetteConfig):
if not isinstance(config, _SilhouetteConfig):
msg = 'Configuration type must be `_SilhouetteConfig`' # type: ignore[unreachable]
raise TypeError(msg)
self._silhouette = config
@property
def slider_styles(self) -> _SliderConfig: # numpydoc ignore=RT01
"""Return the default slider style configurations."""
return self._slider_styles
@slider_styles.setter
def slider_styles(self, config: _SliderConfig):
if not isinstance(config, _SliderConfig):
msg = 'Configuration type must be `_SliderConfig`.' # type: ignore[unreachable]
raise TypeError(msg)
self._slider_styles = config
@property
def axes(self) -> _AxesConfig: # numpydoc ignore=RT01
"""Return or set the default ``axes`` configuration.
Examples
--------
Set the x-axis color to black.
>>> import pyvista as pv
>>> pv.global_theme.axes.x_color = 'black'
Show the axes orientation widget by default.
>>> pv.global_theme.axes.show = True
Use the :func:`axes orientation box <pyvista.create_axes_orientation_box>`
as the orientation widget.
>>> pv.global_theme.axes.box = True
"""
return self._axes
@axes.setter
def axes(self, config: _AxesConfig):
if not isinstance(config, _AxesConfig):
msg = 'Configuration type must be `_AxesConfig`.' # type: ignore[unreachable]
raise TypeError(msg)
self._axes = config
@property
def before_close_callback(
self,
) -> Callable[[pyvista.Plotter], None]: # numpydoc ignore=RT01
"""Return the default before_close_callback function for Plotter."""
return self._before_close_callback # type: ignore[return-value]
@before_close_callback.setter
def before_close_callback(
self,
value: Callable[[pyvista.Plotter], None],
):
self._before_close_callback = value
@property
def allow_empty_mesh(self) -> bool: # numpydoc ignore=RT01
"""Return or set whether to allow plotting empty meshes.
Examples
--------
Enable plotting of empty meshes.
>>> import pyvista as pv
>>> pv.global_theme.allow_empty_mesh = True
Now add an empty mesh to a plotter
>>> pl = pv.Plotter()
>>> _ = pl.add_mesh(pv.PolyData())
>>> pl.show() # doctest: +SKIP
"""
return self._allow_empty_mesh
@allow_empty_mesh.setter
def allow_empty_mesh(self, allow_empty_mesh: bool):
self._allow_empty_mesh = bool(allow_empty_mesh)
def restore_defaults(self):
"""Restore the theme defaults.
Examples
--------
>>> import pyvista as pv
>>> pv.global_theme.restore_defaults()
"""
self.__init__() # type: ignore[misc]
def __repr__(self):
"""User friendly representation of the current theme."""
txt = [f'{self.name.capitalize()} Theme']
txt.append('-' * len(txt[0]))
parm = {
'Background': 'background',
'Jupyter backend': 'jupyter_backend',
'Full screen': 'full_screen',
'Window size': 'window_size',
'Camera': 'camera',
'Notebook': 'notebook',
'Font': 'font',
'Auto close': 'auto_close',
'Colormap': 'cmap',
'Color': 'color',
'Color Cycler': 'color_cycler',
'NaN color': 'nan_color',
'Edge color': 'edge_color',
'Outline color': 'outline_color',
'Floor color': 'floor_color',
'Colorbar orientation': 'colorbar_orientation',
'Colorbar - horizontal': 'colorbar_horizontal',
'Colorbar - vertical': 'colorbar_vertical',
'Show scalar bar': 'show_scalar_bar',
'Show edges': 'show_edges',
'Lighting': 'lighting',
'Interactive': 'interactive',
'Render points as spheres': 'render_points_as_spheres',
'Transparent Background': 'transparent_background',
'Title': 'title',
'Axes': 'axes',
'Multi-samples': 'multi_samples',
'Multi-renderer Split Pos': 'multi_rendering_splitting_position',
'Volume mapper': 'volume_mapper',
'Smooth shading': 'smooth_shading',
'Depth peeling': 'depth_peeling',
'Silhouette': 'silhouette',
'Slider Styles': 'slider_styles',
'Return Camera Position': 'return_cpos',
'Hidden Line Removal': 'hidden_line_removal',
'Anti-Aliasing': '_anti_aliasing',
'Split sharp edges': '_split_sharp_edges',
'Sharp edges feat. angle': '_sharp_edges_feature_angle',
'Before close callback': '_before_close_callback',
}
for name, attr in parm.items():
setting = getattr(self, attr)
txt.append(f'{name:<25}: {setting}')
return '\n'.join(txt)
@property
def name(self) -> str: # numpydoc ignore=RT01
"""Return or set the name of the theme."""
return self._name
@name.setter
def name(self, name: str):
self._name = name
def load_theme(self, theme: str | Theme) -> None:
"""Overwrite the current theme with a theme.
Parameters
----------
theme : pyvista.plotting.themes.Theme
Theme to use to overwrite this theme.
Examples
--------
Create a custom theme from the default theme and load it into
the global theme of pyvista.
>>> import pyvista as pv
>>> from pyvista.plotting.themes import DocumentTheme
>>> my_theme = DocumentTheme()
>>> my_theme.font.size = 20
>>> my_theme.font.title_size = 40
>>> my_theme.cmap = 'jet'
>>> pv.global_theme.load_theme(my_theme)
>>> pv.global_theme.font.size
20
Create a custom theme from the dark theme and load it into
pyvista.
>>> from pyvista.plotting.themes import DarkTheme
>>> my_theme = DarkTheme()
>>> my_theme.show_edges = True
>>> pv.global_theme.load_theme(my_theme)
>>> pv.global_theme.show_edges
True
"""
if isinstance(theme, str):
theme = load_theme(theme)
if not isinstance(theme, Theme):
msg = '``theme`` must be a pyvista theme like ``pyvista.plotting.themes.Theme``.'
raise TypeError(msg)
for attr_name in Theme.__slots__:
setattr(self, attr_name, getattr(theme, attr_name))
def save(self, filename: str) -> None:
"""Serialize this theme to a json file.
``before_close_callback`` is non-serializable and is omitted.
Parameters
----------
filename : str
Path to save the theme to. Should end in ``'.json'``.
Examples
--------
Export and then load back in a theme.
>>> import pyvista as pv
>>> theme = pv.themes.DocumentTheme()
>>> theme.background = 'white'
>>> theme.save('my_theme.json') # doctest:+SKIP
>>> loaded_theme = pv.load_theme('my_theme.json') # doctest:+SKIP
"""
data = self.to_dict()
# functions are not serializable
del data['before_close_callback']
with Path(filename).open('w') as f:
json.dump(data, f)
@property
def split_sharp_edges(self) -> bool: # numpydoc ignore=RT01
"""Set or return splitting sharp edges.
See :ref:`shading_example` for an example showing split sharp edges.
Examples
--------
Enable the splitting of sharp edges globally.
>>> import pyvista as pv
>>> pv.global_theme.split_sharp_edges = True
>>> pv.global_theme.split_sharp_edges
True
Disable the splitting of sharp edges globally.
>>> import pyvista as pv
>>> pv.global_theme.split_sharp_edges = False
>>> pv.global_theme.split_sharp_edges
False
"""
return self._split_sharp_edges
@split_sharp_edges.setter
def split_sharp_edges(self, value: bool):
self._split_sharp_edges = value
@property
def sharp_edges_feature_angle(self) -> float: # numpydoc ignore=RT01
"""Set or return the angle of the sharp edges feature angle.
See :ref:`shading_example` for an example showing split sharp edges.
Examples
--------
Change the sharp edges feature angle to 45 degrees.
>>> import pyvista as pv
>>> pv.global_theme.sharp_edges_feature_angle = 45.0
>>> pv.global_theme.sharp_edges_feature_angle
45.0
"""
return self._sharp_edges_feature_angle
@sharp_edges_feature_angle.setter
def sharp_edges_feature_angle(self, value: float):
self._sharp_edges_feature_angle = float(value)
@property
def lighting_params(self) -> _LightingConfig: # numpydoc ignore=RT01
"""Return or set the default lighting configuration."""
return self._lighting_params
@lighting_params.setter
def lighting_params(self, config: _LightingConfig):
if not isinstance(config, _LightingConfig):
msg = 'Configuration type must be `_LightingConfig`.' # type: ignore[unreachable]
raise TypeError(msg)
self._lighting_params = config
@property
def resample_environment_texture(self) -> bool | float: # numpydoc ignore=RT01
"""Set or return resampling environment texture.
Resample the environment texture when using
:meth:`~pyvista.Plotter.set_environment_texture`.
Set this to a float to set the sampling rate explicitly or set
to ``True`` to downsample the texture to 1/16th of its original
resolution.
Downsampling the texture can substantially improve performance for
some environments, e.g. headless setups or if GPU support is limited.
.. versionadded:: 0.45
Examples
--------
Enable resampling the environment texture globally.
>>> import pyvista as pv
>>> pv.global_theme.resample_environment_texture = True
>>> pv.global_theme.resample_environment_texture
True
Disable the resampling the environment texture.
>>> import pyvista as pv
>>> pv.global_theme.resample_environment_texture = False
>>> pv.global_theme.resample_environment_texture
False
"""
return self._resample_environment_texture
@resample_environment_texture.setter
def resample_environment_texture(self, value: bool | float):
self._resample_environment_texture = value
@property
def logo_file(self) -> str | None: # numpydoc ignore=RT01
"""Return or set the logo file.
.. note::
:func:`pyvista.Plotter.add_logo_widget` will default to
PyVista's logo if this is unset.
Examples
--------
Set the logo file to a custom logo.
>>> import pyvista as pv
>>> from pyvista import examples
>>> logo_file = examples.download_file('vtk.png')
>>> pv.global_theme.logo_file = logo_file
Now the logo will be used by default for :func:`pyvista.Plotter.add_logo_widget`.
>>> pl = pv.Plotter()
>>> _ = pl.add_logo_widget()
>>> _ = pl.add_mesh(pv.Sphere(), show_edges=True)
>>> pl.show()
"""
return self._logo_file
@logo_file.setter
def logo_file(self, logo_file: str | pathlib.Path | None):
if logo_file is None:
path = None
else:
if not pathlib.Path(logo_file).exists():
msg = f'Logo file ({logo_file}) not found.'
raise FileNotFoundError(msg)
path = str(logo_file)
self._logo_file = path
class DarkTheme(Theme):
"""Dark mode theme.
Black background, "viridis" colormap, tan meshes, white (hidden) edges.
Examples
--------
Make the dark theme the global default.
>>> import pyvista as pv
>>> from pyvista import themes
>>> pv.set_plot_theme(themes.DarkTheme())
Alternatively, set via a string.
>>> pv.set_plot_theme('dark')
"""
def __init__(self):
"""Initialize the theme."""
super().__init__()
self.name = 'dark'
self.background = 'black'
self.cmap = 'viridis'
self.font.color = 'white'
self.show_edges = False
self.color = 'lightblue'
self.outline_color = 'white'
self.edge_color = 'white'
self.axes.x_color = 'tomato'
self.axes.y_color = 'seagreen'
self.axes.z_color = 'blue'
class ParaViewTheme(Theme):
"""A paraview-like theme.
Examples
--------
Make the paraview-like theme the global default.
>>> import pyvista as pv
>>> from pyvista import themes
>>> pv.set_plot_theme(themes.ParaViewTheme())
Alternatively, set via a string.
>>> pv.set_plot_theme('paraview')
"""
def __init__(self):
"""Initialize theme."""
super().__init__()
self.name = 'paraview'
self.background = 'paraview'
self.cmap = 'coolwarm'
self.font.family = 'arial'
self.font.label_size = 16
self.font.color = 'white'
self.show_edges = False
self.color = 'white'
self.outline_color = 'white'
self.edge_color = 'black'
self.axes.x_color = 'tomato'
self.axes.y_color = 'gold'
self.axes.z_color = 'green'
class DocumentTheme(Theme):
"""A document theme well suited for papers and presentations.
This theme uses:
* A white background
* Black fonts
* The "viridis" colormap
* disables edges for surface plots
* Hidden edge removal
Best used for presentations, papers, etc.
Examples
--------
Make the document theme the global default.
>>> import pyvista as pv
>>> from pyvista import themes
>>> pv.set_plot_theme(themes.DocumentTheme())
Alternatively, set via a string.
>>> pv.set_plot_theme('document')
"""
def __init__(self):
"""Initialize the theme."""
super().__init__()
self.name = 'document'
self.background = 'white'
self.cmap = 'viridis'
self.font.size = 18
self.font.title_size = 18
self.font.label_size = 18
self.font.color = 'black'
self.show_edges = False
self.color = 'lightblue'
self.outline_color = 'black'
self.edge_color = 'black'
self.axes.x_color = 'tomato'
self.axes.y_color = 'seagreen'
self.axes.z_color = 'blue'
class DocumentProTheme(DocumentTheme):
"""A more professional document theme.
This theme extends the base document theme with:
* Default color cycling
* Rendering points as spheres
* MSAA anti aliassing
* Depth peeling
"""
def __init__(self):
"""Initialize the theme."""
super().__init__()
self.name = 'document_pro'
self.anti_aliasing = 'ssaa'
self.color_cycler = get_cycler('default')
self.render_points_as_spheres = True
self.multi_samples = 8
self.depth_peeling.number_of_peels = 4
self.depth_peeling.occlusion_ratio = 0.0
self.depth_peeling.enabled = True
class _DocumentBuildTheme(DocumentTheme):
"""Theme used for building the documentation."""
def __init__(self):
"""Initialize the theme."""
super().__init__()
self.name = 'document_build'
self.window_size = [1024, 768]
self.font.size = 22
self.font.label_size = 22
self.font.title_size = 22
self.return_cpos = False
self.resample_environment_texture = True
class _TestingTheme(Theme):
"""Low resolution testing theme for ``pytest``.
Necessary for image regression. Xvfb doesn't support
multi-sampling, it's disabled for consistency between desktops and
remote testing.
Also disables ``return_cpos`` to make it easier for us to write
examples without returning camera positions.
Resampling is also enabled for environment textures since this
can be very slow without a GPU.
"""
def __init__(self):
super().__init__()
self.name = 'testing'
self.multi_samples = 1
self.window_size = [400, 400]
self.axes.show = False
self.return_cpos = False
self.resample_environment_texture = True
class _NATIVE_THEMES(Enum): # noqa: N801
"""Global built-in themes available to PyVista."""
paraview = ParaViewTheme
document = DocumentTheme
document_pro = DocumentProTheme
document_build = _DocumentBuildTheme
dark = DarkTheme
default = document
testing = _TestingTheme
vtk = Theme