# Copyright (C) 2025 the astropix team.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""matplotlib configuration module for the astropix chip.
"""
import sys
from typing import Any
from loguru import logger
import matplotlib
from matplotlib import pyplot as plt
if sys.flags.interactive:
plt.ion()
DEFAULT_FIG_WIDTH = 8.
DEFAULT_FIG_HEIGHT = 6.
DEFAULT_FIG_SIZE = (DEFAULT_FIG_WIDTH, DEFAULT_FIG_HEIGHT)
DEFAULT_COLORS = [
'#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b',
'#e377c2', '#7f7f7f', '#bcbd22', '#17becf'
]
SERIF_FONTS = [
'DejaVu Serif', 'Bitstream Vera Serif', 'New Century Schoolbook',
'Century Schoolbook L', 'Utopia', 'ITC Bookman', 'Bookman',
'Nimbus Roman No9 L', 'Times New Roman', 'Times', 'Palatino', 'Charter',
'serif'
]
SANS_SERIF_FONTS = [
'DejaVu Sans', 'Bitstream Vera Sans', 'Lucida Grande', 'Verdana', 'Geneva',
'Lucid', 'Arial', 'Helvetica', 'Avant Garde', 'sans-serif'
]
CURSIVE_FONTS = [
'Apple Chancery', 'Textile', 'Zapf Chancery', 'Sand', 'Script MT',
'Felipa', 'cursive'
]
FANTASY_FONTS = [
'Comic Sans MS', 'Chicago', 'Charcoal', 'Impact', 'Western', 'Humor Sans',
'xkcd', 'fantasy'
]
MONOSPACE_FONTS = [
'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Andale Mono',
'Nimbus Mono L', 'Courier New', 'Courier', 'Fixed', 'Terminal', 'monospace'
]
[docs]
class PlotCard(dict):
"""Small class reperesenting a text card.
This is essentially a dictionary that is capable of plotting itself on
a matplotlib figure in the form of a multi-line graphic card.
Arguments
---------
data : dict
A dictionary holding the lines to be displayed in the card.
"""
KEY_KWARGS = dict(color='gray', size='x-small', ha='left', va='top')
VALUE_KWARGS = dict(color='black', size='small', ha='left', va='top')
def __init__(self, data: dict = None) -> None:
"""Constructor.
"""
super().__init__()
if data is not None:
for key, value in data.items():
self.add_line(key, value)
[docs]
def add_line(self, key: str, value: float, fmt: str = '%g', units: str = None) -> None:
"""Set the value for a given key.
Arguments
---------
key : str
The key, i.e., the explanatory text for a given value.
value : float, optional
The actual value (if None, a blank line will be added).
fmt : str
The string format to be used to render the value.
units : str
The measurement units for the value.
"""
self[key] = (value, fmt, units)
[docs]
def draw(self, axes=None, x: float = 0.05, y: float = 0.95, line_spacing: float = 0.075,
spacing_ratio: float = 0.75) -> None:
"""Draw the card.
Arguments
---------
x0, y0 : float
The absolute coordinates of the top-left corner of the card.
line_spacing : float
The line spacing in units of the total height of the current axes.
spacing_ratio : float
The fractional line spacing assigned to the key label.
"""
# pylint: disable=invalid-name
if axes is None:
axes = plt.gca()
key_norm = spacing_ratio / (1. + spacing_ratio)
value_norm = 1. - key_norm
for kwargs in (self.KEY_KWARGS, self.VALUE_KWARGS):
kwargs['transform'] = axes.transAxes
for key, (value, fmt, units) in self.items():
if value is None:
y -= 0.5 * line_spacing
continue
axes.text(x, y, key, **self.KEY_KWARGS)
y -= key_norm * line_spacing
value = fmt % value
if units is not None:
value = f'{value} {units}'
axes.text(x, y, value, **self.VALUE_KWARGS)
y -= value_norm * line_spacing
[docs]
def last_line_color(default: str = 'black'):
"""Return the color used to draw the last line
"""
try:
return plt.gca().get_lines()[-1].get_color()
except IndexError:
return default
[docs]
def setup_axes(axes, **kwargs):
"""Setup a generic axes object.
"""
if kwargs.get('logx'):
axes.set_xscale('log')
if kwargs.get('logy'):
axes.set_yscale('log')
xticks = kwargs.get('xticks')
if xticks is not None:
axes.set_xticks(xticks)
yticks = kwargs.get('yticks')
if yticks is not None:
axes.set_yticks(yticks)
xlabel = kwargs.get('xlabel')
if xlabel is not None:
axes.set_xlabel(xlabel)
ylabel = kwargs.get('ylabel')
if ylabel is not None:
axes.set_ylabel(ylabel)
xmin, xmax, ymin, ymax = [kwargs.get(key) for key in ('xmin', 'xmax', 'ymin', 'ymax')]
if xmin is None and xmax is None and ymin is None and ymax is None:
pass
else:
axes.axis([xmin, xmax, ymin, ymax])
if kwargs.get('grids'):
axes.grid(which='both')
if kwargs.get('legend'):
axes.legend()
[docs]
def setup_gca(**kwargs):
"""Setup the axes for the current plot.
"""
setup_axes(plt.gca(), **kwargs)
[docs]
def _set_rc_param(key: str, value: Any):
"""Set the value for a single matplotlib parameter.
The actual command is encapsulated into a try except block because this
is intended to work across different matplotlib versions. If a setting
cannot be applied for whatever reason, this will happily move on.
"""
try:
matplotlib.rcParams[key] = value
except KeyError:
logger.warning(f'Unknown matplotlib rc param {key}, skipping...')
except ValueError as exception:
logger.warning(f'{exception}, skipping...')
[docs]
def setup():
"""Basic system-wide setup.
The vast majority of the settings are taken verbatim from the
matplotlib 2, commit 5285e76:
https://github.com/matplotlib/matplotlib/blob/master/matplotlibrc.template
Note that, although this is designed to provide an experience which is as
consistent as possible across different matplotlib versions, some of the
functionalities are not implemented in older versions, which is why we wrap
each parameter setting into a _set_rc_param() function call.
"""
# pylint: disable=too-many-statements
# http://matplotlib.org/api/artist_api.html#module-matplotlib.lines
_set_rc_param('lines.linewidth', 1.5)
_set_rc_param('lines.linestyle', '-')
_set_rc_param('lines.color', DEFAULT_COLORS[0])
_set_rc_param('lines.marker', 'None')
_set_rc_param('lines.markeredgewidth', 1.0)
_set_rc_param('lines.markersize', 6)
_set_rc_param('lines.dash_joinstyle', 'miter')
_set_rc_param('lines.dash_capstyle', 'butt')
_set_rc_param('lines.solid_joinstyle', 'miter')
_set_rc_param('lines.solid_capstyle', 'projecting')
_set_rc_param('lines.antialiased', True)
_set_rc_param('lines.dashed_pattern', (2.8, 1.2))
_set_rc_param('lines.dashdot_pattern', (4.8, 1.2, 0.8, 1.2))
_set_rc_param('lines.dotted_pattern', (1.1, 1.1))
_set_rc_param('lines.scale_dashes', True)
# Markers.
_set_rc_param('markers.fillstyle', 'full')
# http://matplotlib.org/api/artist_api.html#module-matplotlib.patches
_set_rc_param('patch.linewidth', 1)
_set_rc_param('patch.facecolor', DEFAULT_COLORS[0])
_set_rc_param('patch.edgecolor', 'black')
_set_rc_param('patch.force_edgecolor', True)
_set_rc_param('patch.antialiased', True)
# Hatches
_set_rc_param('hatch.color', 'k')
_set_rc_param('hatch.linewidth', 1.0)
# Boxplot
_set_rc_param('boxplot.notch', False)
_set_rc_param('boxplot.vertical', True)
_set_rc_param('boxplot.whiskers', 1.5)
_set_rc_param('boxplot.bootstrap', None)
_set_rc_param('boxplot.patchartist', False)
_set_rc_param('boxplot.showmeans', False)
_set_rc_param('boxplot.showcaps', True)
_set_rc_param('boxplot.showbox', True)
_set_rc_param('boxplot.showfliers', True)
_set_rc_param('boxplot.meanline', False)
_set_rc_param('boxplot.flierprops.color', 'k')
_set_rc_param('boxplot.flierprops.marker', 'o')
_set_rc_param('boxplot.flierprops.markerfacecolor', 'none')
_set_rc_param('boxplot.flierprops.markeredgecolor', 'k')
_set_rc_param('boxplot.flierprops.markersize', 6)
_set_rc_param('boxplot.flierprops.linestyle', 'none')
_set_rc_param('boxplot.flierprops.linewidth', 1.0)
_set_rc_param('boxplot.boxprops.color', 'k')
_set_rc_param('boxplot.boxprops.linewidth', 1.0)
_set_rc_param('boxplot.boxprops.linestyle', '-')
_set_rc_param('boxplot.whiskerprops.color', 'k')
_set_rc_param('boxplot.whiskerprops.linewidth', 1.0)
_set_rc_param('boxplot.whiskerprops.linestyle', '-')
_set_rc_param('boxplot.capprops.color', 'k')
_set_rc_param('boxplot.capprops.linewidth', 1.0)
_set_rc_param('boxplot.capprops.linestyle', '-')
_set_rc_param('boxplot.medianprops.color', DEFAULT_COLORS[1])
_set_rc_param('boxplot.medianprops.linewidth', 1.0)
_set_rc_param('boxplot.medianprops.linestyle', '-')
_set_rc_param('boxplot.meanprops.color', DEFAULT_COLORS[2])
_set_rc_param('boxplot.meanprops.marker', '^')
_set_rc_param('boxplot.meanprops.markerfacecolor', DEFAULT_COLORS[2])
_set_rc_param('boxplot.meanprops.markeredgecolor', DEFAULT_COLORS[2])
_set_rc_param('boxplot.meanprops.markersize', 6)
_set_rc_param('boxplot.meanprops.linestyle', 'none')
_set_rc_param('boxplot.meanprops.linewidth', 1.0)
# http://matplotlib.org/api/font_manager_api.html for more
_set_rc_param('font.family', 'sans-serif')
_set_rc_param('font.style', 'normal')
_set_rc_param('font.variant', 'normal')
_set_rc_param('font.weight', 'medium')
_set_rc_param('font.stretch', 'normal')
_set_rc_param('font.size', 14.0)
_set_rc_param('font.serif', SERIF_FONTS)
_set_rc_param('font.sans-serif', SANS_SERIF_FONTS)
_set_rc_param('font.cursive', CURSIVE_FONTS)
_set_rc_param('font.fantasy', FANTASY_FONTS)
_set_rc_param('font.monospace', MONOSPACE_FONTS)
# http://matplotlib.org/api/artist_api.html#module-matplotlib.text for more
_set_rc_param('text.color', 'black')
# http://wiki.scipy.org/Cookbook/Matplotlib/UsingTex
_set_rc_param('text.usetex', False)
_set_rc_param('text.hinting', 'auto')
_set_rc_param('text.hinting_factor', 8)
_set_rc_param('text.antialiased', True)
_set_rc_param('mathtext.cal', 'cursive')
_set_rc_param('mathtext.rm', 'serif')
_set_rc_param('mathtext.tt', 'monospace')
_set_rc_param('mathtext.it', 'serif:italic')
_set_rc_param('mathtext.bf', 'serif:bold')
_set_rc_param('mathtext.sf', 'sans')
_set_rc_param('mathtext.fontset', 'stixsans')
_set_rc_param('mathtext.default', 'it')
# http://matplotlib.org/api/axes_api.html#module-matplotlib.axes
_set_rc_param('axes.facecolor', 'white')
_set_rc_param('axes.edgecolor', 'black')
_set_rc_param('axes.linewidth', 1.25)
_set_rc_param('axes.grid', False)
_set_rc_param('axes.titlesize', 'large')
_set_rc_param('axes.titlepad', 6.0)
_set_rc_param('axes.labelsize', 'medium')
_set_rc_param('axes.labelpad', 4.0)
_set_rc_param('axes.labelweight', 'normal')
_set_rc_param('axes.labelcolor', 'black')
_set_rc_param('axes.axisbelow', 'line')
_set_rc_param('axes.formatter.limits', (-7, 7))
_set_rc_param('axes.formatter.use_locale', False)
_set_rc_param('axes.formatter.use_mathtext', False)
_set_rc_param('axes.formatter.min_exponent', 0)
_set_rc_param('axes.formatter.useoffset', True)
_set_rc_param('axes.formatter.offset_threshold', 4)
_set_rc_param('axes.unicode_minus', True)
_set_rc_param('axes.autolimit_mode', 'round_numbers')
_set_rc_param('axes.xmargin', 0.)
_set_rc_param('axes.ymargin', 0.)
_set_rc_param('polaraxes.grid', True)
_set_rc_param('axes3d.grid', True)
# Dates
_set_rc_param('date.autoformatter.year', '%Y')
_set_rc_param('date.autoformatter.month', '%Y-%m')
_set_rc_param('date.autoformatter.day', '%Y-%m-%d')
_set_rc_param('date.autoformatter.hour', ' %m-%d %H')
_set_rc_param('date.autoformatter.minute', '%d %H:%M')
_set_rc_param('date.autoformatter.second', '%H:%M:%S')
_set_rc_param('date.autoformatter.microsecond', '%M:%S.%f')
# see http://matplotlib.org/api/axis_api.html#matplotlib.axis.Tick
_set_rc_param('xtick.top', False)
_set_rc_param('xtick.bottom', True)
_set_rc_param('xtick.major.size', 3.5)
_set_rc_param('xtick.minor.size', 2)
_set_rc_param('xtick.major.width', 1.25)
_set_rc_param('xtick.minor.width', 1.)
_set_rc_param('xtick.major.pad', 3.5)
_set_rc_param('xtick.minor.pad', 3.4)
_set_rc_param('xtick.color', 'k')
_set_rc_param('xtick.labelsize', 'medium')
_set_rc_param('xtick.direction', 'out')
_set_rc_param('xtick.minor.visible', False)
_set_rc_param('xtick.major.top', True)
_set_rc_param('xtick.major.bottom', True)
_set_rc_param('xtick.minor.top', True)
_set_rc_param('xtick.minor.bottom', True)
_set_rc_param('ytick.left', True)
_set_rc_param('ytick.right', False)
_set_rc_param('ytick.major.size', 3.5)
_set_rc_param('ytick.minor.size', 2)
_set_rc_param('ytick.major.width', 1.25)
_set_rc_param('ytick.minor.width', 1.)
_set_rc_param('ytick.major.pad', 3.5)
_set_rc_param('ytick.minor.pad', 3.4)
_set_rc_param('ytick.color', 'k')
_set_rc_param('ytick.labelsize', 'medium')
_set_rc_param('ytick.direction', 'out')
_set_rc_param('ytick.minor.visible', False)
_set_rc_param('ytick.major.left', True)
_set_rc_param('ytick.major.right', True)
_set_rc_param('ytick.minor.left', True)
_set_rc_param('ytick.minor.right', True)
# Grids
_set_rc_param('grid.color', '#c0c0c0')
_set_rc_param('grid.linestyle', '--')
_set_rc_param('grid.linewidth', 0.8)
_set_rc_param('grid.alpha', 1.0)
# Legend
_set_rc_param('legend.loc', 'best')
_set_rc_param('legend.frameon', True)
_set_rc_param('legend.framealpha', 0.8)
_set_rc_param('legend.facecolor', 'inherit')
_set_rc_param('legend.edgecolor', 'gray')
_set_rc_param('legend.fancybox', True)
_set_rc_param('legend.shadow', False)
_set_rc_param('legend.numpoints', 1)
_set_rc_param('legend.scatterpoints', 1)
_set_rc_param('legend.markerscale', 1.0)
_set_rc_param('legend.fontsize', 'medium')
_set_rc_param('legend.borderpad', 0.4)
_set_rc_param('legend.labelspacing', 0.5)
_set_rc_param('legend.handlelength', 2.0)
_set_rc_param('legend.handleheight', 0.7)
_set_rc_param('legend.handletextpad', 0.8)
_set_rc_param('legend.borderaxespad', 0.5)
_set_rc_param('legend.columnspacing', 2.0)
# See http://matplotlib.org/api/figure_api.html#matplotlib.figure.Figure
_set_rc_param('figure.titlesize', 'large')
_set_rc_param('figure.titleweight', 'normal')
_set_rc_param('figure.figsize', DEFAULT_FIG_SIZE)
_set_rc_param('figure.dpi', 100)
_set_rc_param('figure.facecolor', 'white')
_set_rc_param('figure.edgecolor', 'white')
_set_rc_param('figure.autolayout', False)
_set_rc_param('figure.max_open_warning', 20)
_set_rc_param('figure.subplot.left', 0.125)
_set_rc_param('figure.subplot.right', 0.95)
_set_rc_param('figure.subplot.bottom', 0.10)
_set_rc_param('figure.subplot.top', 0.95)
_set_rc_param('figure.subplot.wspace', 0.2)
_set_rc_param('figure.subplot.hspace', 0.2)
# Images
_set_rc_param('image.aspect', 'equal')
_set_rc_param('image.interpolation', 'nearest')
_set_rc_param('image.cmap', 'jet')
_set_rc_param('image.lut', 256)
_set_rc_param('image.origin', 'upper')
_set_rc_param('image.resample', True)
_set_rc_param('image.composite_image', True)
# Contour plots
_set_rc_param('contour.negative_linestyle', 'dashed')
_set_rc_param('contour.corner_mask', True)
# Errorbar plots
_set_rc_param('errorbar.capsize', 0)
# Histogram plots
_set_rc_param('hist.bins', 10)
# Scatter plots
_set_rc_param('scatter.marker', 'o')
# Saving figures
_set_rc_param('path.simplify', True)
_set_rc_param('path.simplify_threshold', 0.1)
_set_rc_param('path.snap', True)
_set_rc_param('path.sketch', None)
_set_rc_param('savefig.dpi', 'figure')
_set_rc_param('savefig.facecolor', 'white')
_set_rc_param('savefig.edgecolor', 'white')
_set_rc_param('savefig.format', 'png')
_set_rc_param('savefig.bbox', 'standard')
_set_rc_param('savefig.pad_inches', 0.1)
_set_rc_param('savefig.directory', '~')
_set_rc_param('savefig.transparent', False)
# Back-ends
_set_rc_param('pdf.compression', 6)
_set_rc_param('pdf.fonttype', 3)
_set_rc_param('svg.image_inline', True)
_set_rc_param('svg.fonttype', 'path')
_set_rc_param('svg.hashsalt', None)
# Key maps
_set_rc_param('keymap.fullscreen', ('f', 'ctrl+f'))
_set_rc_param('keymap.home', ('h', 'r', 'home'))
_set_rc_param('keymap.back', ('left', 'c', 'backspace'))
_set_rc_param('keymap.forward', ('right', 'v'))
_set_rc_param('keymap.pan', 'p')
_set_rc_param('keymap.zoom', 'o')
_set_rc_param('keymap.save', 's')
_set_rc_param('keymap.quit', ('ctrl+w', 'cmd+w'))
_set_rc_param('keymap.grid', 'g')
_set_rc_param('keymap.grid_minor', 'G')
_set_rc_param('keymap.yscale', 'l')
_set_rc_param('keymap.xscale', ('L', 'k'))
# Animations settings
_set_rc_param('animation.html', 'none')
_set_rc_param('animation.writer', 'ffmpeg')
_set_rc_param('animation.codec', 'h264')
_set_rc_param('animation.bitrate', -1)
_set_rc_param('animation.frame_format', 'png')
_set_rc_param('animation.ffmpeg_path', 'ffmpeg')
_set_rc_param('animation.ffmpeg_args', '')
_set_rc_param('animation.convert_path', 'convert')
# Miscellanea
_set_rc_param('timezone', 'UTC')
setup()