12cf2e8247
Add IsEvadedOffense to EFPenalty Fix remote log reading in not Windows
533 lines
15 KiB
Python
533 lines
15 KiB
Python
# -*- coding: utf-8 -*-
|
|
# This file is part of pygal
|
|
#
|
|
# A python svg graph plotting library
|
|
# Copyright © 2012-2016 Kozea
|
|
#
|
|
# This library is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Lesser General Public License as published by the Free
|
|
# Software Foundation, either version 3 of the License, or (at your option) any
|
|
# later version.
|
|
#
|
|
# This library 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 Lesser General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public License
|
|
# along with pygal. If not, see <http://www.gnu.org/licenses/>.
|
|
"""Charts styling classes"""
|
|
|
|
from __future__ import division
|
|
|
|
from itertools import chain
|
|
|
|
from pygal import colors
|
|
from pygal.colors import darken, is_foreground_light, lighten
|
|
|
|
|
|
class Style(object):
|
|
|
|
"""Styling class containing colors for the css generation"""
|
|
|
|
plot_background = 'rgba(255, 255, 255, 1)'
|
|
background = 'rgba(249, 249, 249, 1)'
|
|
value_background = 'rgba(229, 229, 229, 1)'
|
|
foreground = 'rgba(0, 0, 0, .87)'
|
|
foreground_strong = 'rgba(0, 0, 0, 1)'
|
|
foreground_subtle = 'rgba(0, 0, 0, .54)'
|
|
|
|
# Monospaced font is highly encouraged
|
|
font_family = (
|
|
'Consolas, "Liberation Mono", Menlo, Courier, monospace')
|
|
|
|
label_font_family = None
|
|
major_label_font_family = None
|
|
value_font_family = None
|
|
value_label_font_family = None
|
|
tooltip_font_family = None
|
|
title_font_family = None
|
|
legend_font_family = None
|
|
no_data_font_family = None
|
|
|
|
label_font_size = 10
|
|
major_label_font_size = 10
|
|
value_font_size = 16
|
|
value_label_font_size = 10
|
|
tooltip_font_size = 14
|
|
title_font_size = 16
|
|
legend_font_size = 14
|
|
no_data_font_size = 64
|
|
|
|
# Guide line dash array style
|
|
guide_stroke_dasharray = '4,4'
|
|
major_guide_stroke_dasharray = '6,6'
|
|
|
|
opacity = '.7'
|
|
opacity_hover = '.8'
|
|
|
|
stroke_opacity = '.8'
|
|
stroke_opacity_hover = '.9'
|
|
|
|
transition = '150ms'
|
|
colors = (
|
|
'#F44336', # 0
|
|
'#3F51B5', # 4
|
|
'#009688', # 8
|
|
'#FFC107', # 13
|
|
'#FF5722', # 15
|
|
'#9C27B0', # 2
|
|
'#03A9F4', # 6
|
|
'#8BC34A', # 10
|
|
'#FF9800', # 14
|
|
'#E91E63', # 1
|
|
'#2196F3', # 5
|
|
'#4CAF50', # 9
|
|
'#FFEB3B', # 12
|
|
'#673AB7', # 3
|
|
'#00BCD4', # 7
|
|
'#CDDC39', # 11b
|
|
'#9E9E9E', # 17
|
|
'#607D8B', # 18
|
|
)
|
|
|
|
value_colors = ()
|
|
ci_colors = ()
|
|
|
|
def __init__(self, **kwargs):
|
|
"""Create the style"""
|
|
self.__dict__.update(kwargs)
|
|
self._google_fonts = set()
|
|
if self.font_family.startswith('googlefont:'):
|
|
self.font_family = self.font_family.replace('googlefont:', '')
|
|
self._google_fonts.add(self.font_family.split(',')[0].strip())
|
|
|
|
for name in dir(self):
|
|
if name.endswith('_font_family'):
|
|
fn = getattr(self, name)
|
|
if fn is None:
|
|
setattr(self, name, self.font_family)
|
|
elif fn.startswith('googlefont:'):
|
|
setattr(self, name, fn.replace('googlefont:', ''))
|
|
self._google_fonts.add(
|
|
getattr(self, name).split(',')[0].strip())
|
|
|
|
def get_colors(self, prefix, len_):
|
|
"""Get the css color list"""
|
|
def color(tupl):
|
|
"""Make a color css"""
|
|
return ((
|
|
'%s.color-{0}, %s.color-{0} a:visited {{\n'
|
|
' stroke: {1};\n'
|
|
' fill: {1};\n'
|
|
'}}\n') % (prefix, prefix)).format(*tupl)
|
|
|
|
def value_color(tupl):
|
|
"""Make a value color css"""
|
|
return ((
|
|
'%s .text-overlay .color-{0} text {{\n'
|
|
' fill: {1};\n'
|
|
'}}\n') % (prefix,)).format(*tupl)
|
|
|
|
def ci_color(tupl):
|
|
"""Make a value color css"""
|
|
if not tupl[1]:
|
|
return ''
|
|
return ((
|
|
'%s .color-{0} .ci {{\n'
|
|
' stroke: {1};\n'
|
|
'}}\n') % (prefix,)).format(*tupl)
|
|
|
|
if len(self.colors) < len_:
|
|
missing = len_ - len(self.colors)
|
|
cycles = 1 + missing // len(self.colors)
|
|
colors = []
|
|
for i in range(0, cycles + 1):
|
|
for color_ in self.colors:
|
|
colors.append(darken(color_, 33 * i / cycles))
|
|
if len(colors) >= len_:
|
|
break
|
|
else:
|
|
continue
|
|
break
|
|
else:
|
|
colors = self.colors[:len_]
|
|
|
|
# Auto compute foreground value color when color is missing
|
|
value_colors = []
|
|
for i in range(len_):
|
|
if i < len(self.value_colors) and self.value_colors[i] is not None:
|
|
value_colors.append(self.value_colors[i])
|
|
else:
|
|
value_colors.append('white' if is_foreground_light(
|
|
colors[i]) else 'black')
|
|
|
|
return '\n'.join(chain(
|
|
map(color, enumerate(colors)),
|
|
map(value_color, enumerate(value_colors)),
|
|
map(ci_color, enumerate(self.ci_colors))))
|
|
|
|
def to_dict(self):
|
|
"""Convert instance to a serializable mapping."""
|
|
config = {}
|
|
for attr in dir(self):
|
|
if not attr.startswith('_'):
|
|
value = getattr(self, attr)
|
|
if not hasattr(value, '__call__'):
|
|
config[attr] = value
|
|
return config
|
|
|
|
|
|
DefaultStyle = Style
|
|
|
|
|
|
class DarkStyle(Style):
|
|
|
|
"""A dark style (old default)"""
|
|
|
|
background = 'black'
|
|
plot_background = '#111'
|
|
foreground = '#999'
|
|
foreground_strong = '#eee'
|
|
foreground_subtle = '#555'
|
|
opacity = '.8'
|
|
opacity_hover = '.4'
|
|
transition = '250ms'
|
|
colors = (
|
|
'#ff5995', '#b6e354', '#feed6c', '#8cedff', '#9e6ffe',
|
|
'#899ca1', '#f8f8f2', '#bf4646', '#516083', '#f92672',
|
|
'#82b414', '#fd971f', '#56c2d6', '#808384', '#8c54fe',
|
|
'#465457')
|
|
|
|
|
|
class LightStyle(Style):
|
|
|
|
"""A light style"""
|
|
|
|
background = 'white'
|
|
plot_background = 'rgba(0, 0, 255, 0.1)'
|
|
foreground = 'rgba(0, 0, 0, 0.7)'
|
|
foreground_strong = 'rgba(0, 0, 0, 0.9)'
|
|
foreground_subtle = 'rgba(0, 0, 0, 0.5)'
|
|
colors = ('#242424', '#9f6767', '#92ac68',
|
|
'#d0d293', '#9aacc3', '#bb77a4',
|
|
'#77bbb5', '#777777')
|
|
|
|
|
|
class NeonStyle(DarkStyle):
|
|
|
|
"""Similar to DarkStyle but with more opacity and effects"""
|
|
|
|
opacity = '.1'
|
|
opacity_hover = '.75'
|
|
transition = '1s ease-out'
|
|
|
|
|
|
class CleanStyle(Style):
|
|
|
|
"""A rather clean style"""
|
|
|
|
background = 'transparent'
|
|
plot_background = 'rgba(240, 240, 240, 0.7)'
|
|
foreground = 'rgba(0, 0, 0, 0.9)'
|
|
foreground_strong = 'rgba(0, 0, 0, 0.9)'
|
|
foreground_subtle = 'rgba(0, 0, 0, 0.5)'
|
|
colors = (
|
|
'rgb(12,55,149)', 'rgb(117,38,65)', 'rgb(228,127,0)', 'rgb(159,170,0)',
|
|
'rgb(149,12,12)')
|
|
|
|
|
|
class DarkSolarizedStyle(Style):
|
|
|
|
"""Dark solarized popular theme"""
|
|
|
|
background = '#073642'
|
|
plot_background = '#002b36'
|
|
foreground = '#839496'
|
|
foreground_strong = '#fdf6e3'
|
|
foreground_subtle = '#657b83'
|
|
opacity = '.66'
|
|
opacity_hover = '.9'
|
|
transition = '500ms ease-in'
|
|
colors = (
|
|
'#b58900', '#cb4b16', '#dc322f', '#d33682',
|
|
'#6c71c4', '#268bd2', '#2aa198', '#859900')
|
|
|
|
|
|
class LightSolarizedStyle(DarkSolarizedStyle):
|
|
|
|
"""Light solarized popular theme"""
|
|
|
|
background = '#fdf6e3'
|
|
plot_background = '#eee8d5'
|
|
foreground = '#657b83'
|
|
foreground_strong = '#073642'
|
|
foreground_subtle = '#073642'
|
|
|
|
|
|
class RedBlueStyle(Style):
|
|
|
|
"""A red and blue theme"""
|
|
|
|
background = lighten('#e6e7e9', 7)
|
|
plot_background = lighten('#e6e7e9', 10)
|
|
foreground = 'rgba(0, 0, 0, 0.9)'
|
|
foreground_strong = 'rgba(0, 0, 0, 0.9)'
|
|
foreground_subtle = 'rgba(0, 0, 0, 0.5)'
|
|
opacity = '.6'
|
|
opacity_hover = '.9'
|
|
colors = (
|
|
'#d94e4c', '#e5884f', '#39929a',
|
|
lighten('#d94e4c', 10), darken('#39929a', 15), lighten('#e5884f', 17),
|
|
darken('#d94e4c', 10), '#234547')
|
|
|
|
|
|
class LightColorizedStyle(Style):
|
|
|
|
"""A light colorized style"""
|
|
|
|
background = '#f8f8f8'
|
|
plot_background = lighten('#f8f8f8', 3)
|
|
foreground = '#333'
|
|
foreground_strong = '#666'
|
|
foreground_subtle = 'rgba(0, 0 , 0, 0.5)'
|
|
opacity = '.5'
|
|
opacity_hover = '.9'
|
|
transition = '250ms ease-in'
|
|
colors = (
|
|
'#fe9592', '#534f4c', '#3ac2c0', '#a2a7a1',
|
|
darken('#fe9592', 15), lighten('#534f4c', 15), lighten('#3ac2c0', 15),
|
|
lighten('#a2a7a1', 15), lighten('#fe9592', 15), darken('#3ac2c0', 10))
|
|
|
|
|
|
class DarkColorizedStyle(Style):
|
|
|
|
"""A dark colorized style"""
|
|
|
|
background = darken('#3a2d3f', 5)
|
|
plot_background = lighten('#3a2d3f', 2)
|
|
foreground = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_strong = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_subtle = 'rgba(255, 255 , 255, 0.5)'
|
|
opacity = '.2'
|
|
opacity_hover = '.7'
|
|
transition = '250ms ease-in'
|
|
colors = (
|
|
'#c900fe', '#01b8fe', '#59f500', '#ff00e4', '#f9fa00',
|
|
darken('#c900fe', 20), darken('#01b8fe', 15), darken('#59f500', 20),
|
|
darken('#ff00e4', 15), lighten('#f9fa00', 20))
|
|
|
|
|
|
class TurquoiseStyle(Style):
|
|
|
|
"""A turquoise style"""
|
|
|
|
background = darken('#1b8088', 15)
|
|
plot_background = darken('#1b8088', 17)
|
|
foreground = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_strong = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_subtle = 'rgba(255, 255 , 255, 0.5)'
|
|
opacity = '.5'
|
|
opacity_hover = '.9'
|
|
transition = '250ms ease-in'
|
|
colors = (
|
|
'#93d2d9', '#ef940f', '#8C6243', '#fff',
|
|
darken('#93d2d9', 20), lighten('#ef940f', 15),
|
|
lighten('#8c6243', 15), '#1b8088')
|
|
|
|
|
|
class LightGreenStyle(Style):
|
|
|
|
"""A light green style"""
|
|
|
|
background = lighten('#f3f3f3', 3)
|
|
plot_background = '#fff'
|
|
foreground = '#333333'
|
|
foreground_strong = '#666'
|
|
foreground_subtle = '#222222'
|
|
opacity = '.5'
|
|
opacity_hover = '.9'
|
|
transition = '250ms ease-in'
|
|
colors = (
|
|
'#7dcf30', '#247fab', lighten('#7dcf30', 10), '#ccc',
|
|
darken('#7dcf30', 15), '#ddd', lighten('#247fab', 10),
|
|
darken('#247fab', 15))
|
|
|
|
|
|
class DarkGreenStyle(Style):
|
|
|
|
"""A dark green style"""
|
|
|
|
background = darken('#251e01', 3)
|
|
plot_background = darken('#251e01', 1)
|
|
foreground = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_strong = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_subtle = 'rgba(255, 255, 255, 0.6)'
|
|
opacity = '.6'
|
|
opacity_hover = '.9'
|
|
transition = '250ms ease-in'
|
|
colors = (
|
|
'#adde09', '#6e8c06', '#4a5e04', '#fcd202', '#C1E34D',
|
|
lighten('#fcd202', 25))
|
|
|
|
|
|
class DarkGreenBlueStyle(Style):
|
|
|
|
"""A dark green and blue style"""
|
|
|
|
background = '#000'
|
|
plot_background = lighten('#000', 8)
|
|
foreground = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_strong = 'rgba(255, 255, 255, 0.9)'
|
|
foreground_subtle = 'rgba(255, 255, 255, 0.6)'
|
|
opacity = '.55'
|
|
opacity_hover = '.9'
|
|
transition = '250ms ease-in'
|
|
colors = (lighten('#34B8F7', 15), '#7dcf30', '#247fab',
|
|
darken('#7dcf30', 10), lighten('#247fab', 10),
|
|
lighten('#7dcf30', 10), darken('#247fab', 10), '#fff')
|
|
|
|
|
|
class BlueStyle(Style):
|
|
|
|
"""A blue style"""
|
|
|
|
background = darken('#f8f8f8', 3)
|
|
plot_background = '#f8f8f8'
|
|
foreground = 'rgba(0, 0, 0, 0.9)'
|
|
foreground_strong = 'rgba(0, 0, 0, 0.9)'
|
|
foreground_subtle = 'rgba(0, 0, 0, 0.6)'
|
|
opacity = '.5'
|
|
opacity_hover = '.9'
|
|
transition = '250ms ease-in'
|
|
colors = (
|
|
'#00b2f0', '#43d9be', '#0662ab', darken('#00b2f0', 20),
|
|
lighten('#43d9be', 20), lighten('#7dcf30', 10), darken('#0662ab', 15),
|
|
'#ffd541', '#7dcf30', lighten('#00b2f0', 15), darken('#ffd541', 20))
|
|
|
|
|
|
class SolidColorStyle(Style):
|
|
|
|
"""A light style with strong colors"""
|
|
|
|
background = '#FFFFFF'
|
|
plot_background = '#FFFFFF'
|
|
foreground = '#000000'
|
|
foreground_strong = '#000000'
|
|
foreground_subtle = '#828282'
|
|
opacity = '.8'
|
|
opacity_hover = '.9'
|
|
transition = '400ms ease-in'
|
|
colors = (
|
|
'#FF9900', '#DC3912', '#4674D1', '#109618', '#990099',
|
|
'#0099C6', '#DD4477', '#74B217', '#B82E2E', '#316395', '#994499')
|
|
|
|
|
|
styles = {'default': DefaultStyle,
|
|
'dark': DarkStyle,
|
|
'light': LightStyle,
|
|
'neon': NeonStyle,
|
|
'clean': CleanStyle,
|
|
'light_red_blue': RedBlueStyle,
|
|
'dark_solarized': DarkSolarizedStyle,
|
|
'light_solarized': LightSolarizedStyle,
|
|
'dark_colorized': DarkColorizedStyle,
|
|
'light_colorized': LightColorizedStyle,
|
|
'turquoise': TurquoiseStyle,
|
|
'green': LightGreenStyle,
|
|
'dark_green': DarkGreenStyle,
|
|
'dark_green_blue': DarkGreenBlueStyle,
|
|
'blue': BlueStyle,
|
|
'solid_color': SolidColorStyle}
|
|
|
|
|
|
class ParametricStyleBase(Style):
|
|
|
|
"""Parametric Style base class for all the parametric operations"""
|
|
|
|
_op = None
|
|
|
|
def __init__(self, color, step=10, max_=None, base_style=None, **kwargs):
|
|
"""
|
|
Initialization of the parametric style.
|
|
|
|
This takes several parameters:
|
|
* a `step` which correspond on how many colors will be needed
|
|
* a `max_` which defines the maximum amplitude of the color effect
|
|
* a `base_style` which will be taken as default for everything
|
|
except colors
|
|
* any keyword arguments setting other style parameters
|
|
"""
|
|
|
|
if self._op is None:
|
|
raise RuntimeError('ParametricStyle is not instanciable')
|
|
|
|
defaults = {}
|
|
if base_style is not None:
|
|
if isinstance(base_style, type):
|
|
base_style = base_style()
|
|
defaults.update(base_style.to_dict())
|
|
defaults.update(kwargs)
|
|
|
|
super(ParametricStyleBase, self).__init__(**defaults)
|
|
|
|
if max_ is None:
|
|
violency = {
|
|
'darken': 50,
|
|
'lighten': 50,
|
|
'saturate': 100,
|
|
'desaturate': 100,
|
|
'rotate': 360
|
|
}
|
|
max_ = violency[self._op]
|
|
|
|
def modifier(index):
|
|
percent = max_ * index / (step - 1)
|
|
return getattr(colors, self._op)(color, percent)
|
|
|
|
self.colors = list(map(modifier, range(0, max(2, step))))
|
|
|
|
|
|
class LightenStyle(ParametricStyleBase):
|
|
|
|
"""Create a style by lightening the given color"""
|
|
|
|
_op = 'lighten'
|
|
|
|
|
|
class DarkenStyle(ParametricStyleBase):
|
|
|
|
"""Create a style by darkening the given color"""
|
|
|
|
_op = 'darken'
|
|
|
|
|
|
class SaturateStyle(ParametricStyleBase):
|
|
|
|
"""Create a style by saturating the given color"""
|
|
|
|
_op = 'saturate'
|
|
|
|
|
|
class DesaturateStyle(ParametricStyleBase):
|
|
|
|
"""Create a style by desaturating the given color"""
|
|
|
|
_op = 'desaturate'
|
|
|
|
|
|
class RotateStyle(ParametricStyleBase):
|
|
|
|
"""Create a style by rotating the given color"""
|
|
|
|
_op = 'rotate'
|
|
|
|
|
|
parametric_styles = {
|
|
'lighten': LightenStyle,
|
|
'darken': DarkenStyle,
|
|
'saturate': SaturateStyle,
|
|
'desaturate': DesaturateStyle,
|
|
'rotate': RotateStyle
|
|
}
|