"""Module containing predefined plot styles.
For reference see:
https://glotaran.github.io/legacy/plot_styles
"""
from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING
import matplotlib.colors as colors
import matplotlib.pyplot as plt
from cycler import cycler
if TYPE_CHECKING:
from cycler import Cycler
[docs]
class ColorCode(str, Enum):
"""Color definitions from legacy glotaran.
See: https://glotaran.github.io/legacy/plot_styles
"""
# Name #Hex
black = "#000000"
red = "#ff0000"
blue = "#0000ff"
green = "#00ff00"
magenta = "#ff00ff"
cyan = "#00ffff"
yellow = "#ffff00"
green4 = "#008b00"
orange = "#ff8c00"
brown = "#964b00"
grey = "#808080"
violet = "#9400d3"
turquoise = "#40e0d0"
maroon = "#800000"
indigo = "#4b0082"
[docs]
@staticmethod
def hex_to_rgb(hex_string: str) -> tuple[int, ...]:
"""Convert hex code to rgb or rgba tuple.
Parameters
----------
hex_string : str
Hey code representation of a color.
Returns
-------
tuple[int, ...]
rgb or rgba tuple representing the same color as ``hex_string``..
"""
rgb = colors.hex2color(hex_string)
return tuple(int(255 * x) for x in rgb)
[docs]
@staticmethod
def rgb_to_hex(rgb_tuple: tuple[float, ...]) -> str:
"""Convert rgb value tuple to hex code.
Parameters
----------
rgb_tuple : tuple[float, ...]
Tuple rgb or rgba values
Returns
-------
str
Hex code representing the same color as ``rgb_tuple``.
"""
return colors.rgb2hex([1.0 * x / 255 for x in rgb_tuple])
[docs]
class LineStyle(str, Enum):
"""Subset of line styles supported by matplotlib."""
solid = "-"
dashed = "--"
dotted = ".."
dashdot = "-."
[docs]
class DataColorCode(str, Enum):
"""Colors used to plot data and fits.
Pairs of visually similar looking colors whereby the first (lighter) color is used to plot the
data, and the second (darker) color is used to represent the fitted trace (that goes 'through'
the data).
"""
# Name #Hex
grey = "#808080"
black = "#000000"
orange = "#ff8c00"
red = "#ff0000"
cyan = "#00ffff"
blue = "#0000ff"
green = "#00ff00"
green4 = "#008b00"
magenta = "#ff00ff"
indigo = "#4b0082"
brown = "#964b00"
maroon = "#800000"
yellow = "#ffff00"
# Adding orange a second time needs to be added manually to the list in PlotStyle
# since Enum doesn't allow duplicates
[docs]
class DataLineStyle(str, Enum):
"""Data plots can alternate between solid lines for data and dashed lines for fits.
This is mostly useful for data with very low noise (e.g. simulated data), since data and fit
often overlap.
"""
solid = "-"
dashed = "--"
[docs]
class PlotStyle:
"""Wrapper class to hold predefined Plot styles."""
def __init__(self) -> None:
"""Initialize Stiles from Enums."""
self._line_style = list(LineStyle)
self._color_codes = list(ColorCode)
# Since Enum only supports unique values we need to manually add the last one
self._data_color_codes = [*list(DataColorCode), DataColorCode.orange]
# Extend linecycler to same size as colorcycler
self._data_line_style = list(DataLineStyle) * (len(self._data_color_codes) // 2)
self.SMALL_SIZE = 12
self.MEDIUM_SIZE = 14
self.BIGGER_SIZE = 18
[docs]
def set_default_fontsize(self) -> None:
"""Set global plot settings for matplotlib."""
plt.rc("font", size=self.SMALL_SIZE) # controls default text sizes
plt.rc("axes", titlesize=self.SMALL_SIZE) # fontsize of the axes title
plt.rc("axes", labelsize=self.MEDIUM_SIZE) # fontsize of the x and y labels
plt.rc("xtick", labelsize=self.SMALL_SIZE) # fontsize of the tick labels
plt.rc("ytick", labelsize=self.SMALL_SIZE) # fontsize of the tick labels
plt.rc("legend", fontsize=self.SMALL_SIZE) # legend fontsize
plt.rc("figure", titlesize=self.BIGGER_SIZE) # fontsize of the figure title
[docs]
def set_default_colors(self) -> None:
"""Set default color cycler for matplotlib."""
plt.rc("axes", prop_cycle=self.cycler)
@property
def cycler(self) -> Cycler:
"""Cycle for default legacy like plots using color defined in :class:`ColorCode`.
Returns
-------
Cycler
Plot color cycler for general plots.
"""
return cycler("color", self._color_codes)
@property
def data_cycler_solid_dashed(self) -> Cycler:
"""Cycle that alternates between solid and dashes and uses :class:`DataColorCode`.
This is useful when plotting simulation or very low noise data, where the
fit practically falls on top and hides the data lines.
Returns
-------
Cycler
Plot color cycler for low noise data+fit plots.
"""
return cycler(color=self._data_color_codes, linestyle=self._data_line_style)
@property
def data_cycler_solid(self) -> Cycler:
"""Color cycler using :class:`DataColorCode`.
Returns
-------
Cycler
Plot color cycler for data+fit plots.
"""
return cycler(color=self._data_color_codes)
@property
def line_cycler(self) -> Cycler:
"""Cycle iterating over line styles defined in :class:`LineStyle`.
Returns
-------
Cycler
Plot color cycler for multi line style plots.
"""
return cycler("linestyle", self._line_style)