from typing import Optional, List
from decimal import Decimal
from collections import UserDict
try:
import orjson as json
except ImportError:
try:
import rapidjson as json
except ImportError:
try:
import simplejson as json
except ImportError:
import json
from validator_collection import validators, checkers, errors as validator_errors
from highcharts_core import constants, errors, utility_functions
from highcharts_core.decorators import validate_types, class_sensitive
from highcharts_core.metaclasses import HighchartsMeta, JavaScriptDict
from highcharts_core.utility_classes.gradients import Gradient
from highcharts_core.utility_classes.patterns import Pattern
from highcharts_core.utility_classes.javascript_functions import CallbackFunction
from highcharts_core.utility_classes.ast import AttributeObject
from highcharts_core.utility_classes.states import States
class CollapseButtonConfiguration(HighchartsMeta):
"""Configuration options that apply to the Collapse button used in certain series types."""
def __init__(self, **kwargs):
self._enabled = None
self._height = None
self._line_width = None
self._only_on_hover = None
self._shape = None
self._style = None
self._width = None
self._x = None
self._y = None
self.enabled = kwargs.get('enabled', None)
self.height = kwargs.get('height', None)
self.line_width = kwargs.get('line_width', None)
self.only_on_hover = kwargs.get('only_on_hover', None)
self.shape = kwargs.get('shape', None)
self.style = kwargs.get('style', None)
self.width = kwargs.get('width', None)
self.x = kwargs.get('x', None)
self.y = kwargs.get('y', None)
@property
def enabled(self) -> Optional[bool]:
"""If ``True``, displays the button. If ``False``, the button will be hidden.
Defaults to ``True``.
:returns: Flag indicating whether the button is displayed on the chart.
:rtype: :class:`bool <python:bool>` or :obj:`None <python:None>`
"""
return self._enabled
@enabled.setter
def enabled(self, value):
if value is None:
self._enabled = None
else:
self._enabled = bool(value)
@property
def height(self) -> Optional[int | float | Decimal]:
"""The height of the button, expressed in pixels. Defaults to ``10``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._height
@height.setter
def height(self, value):
if value is None:
self._height = None
else:
self._height = validators.numeric(value,
allow_empty = False,
minimum = 0)
@property
def line_width(self) -> Optional[int | float | Decimal]:
"""The line_width of the button, expressed in pixels. Defaults to ``1``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._line_width
@line_width.setter
def line_width(self, value):
if value is None:
self._line_width = None
else:
self._line_width = validators.numeric(value,
allow_empty = False,
minimum = 0)
@property
def only_on_hover(self) -> Optional[bool]:
"""Whether the button should be visible only when the node is hovered. Defaults to ``True``.
.. note::
When set to ``True``, the button is hidden for uncollapsed nodes and shown for collapsed nodes.
:rtype: :class:`bool <python:bool>` or :obj:`None <python:None>`
"""
return self._only_on_hover
@only_on_hover.setter
def only_on_hover(self, value):
if value is None:
self._only_on_hover = None
else:
self._only_on_hover = bool(value)
@property
def shape(self) -> Optional[str]:
"""The symbol to use on the collapse button. Defaults to ``'circle'``.
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._shape
@shape.setter
def shape(self, value):
self._shape = validators.string(value, allow_empty = True)
@property
def style(self) -> Optional[dict]:
"""CSS styles for the collapse button.
.. note::
In styled mode, the collapse button style is given in the ``.highcharts-collapse-button`` CSS class.
:rtype: :class:`dict <python:dict>` or :obj:`None <python:None>`
"""
return self._style
@style.setter
def style(self, value):
self._value = validators.dict(value, allow_empty = True)
@property
def width(self) -> Optional[int | float | Decimal]:
"""The width of the button, expressed in pixels. Defaults to ``10``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._width
@width.setter
def width(self, value):
self._width = validators.numeric(value,
allow_empty = True,
minimum = 0)
@property
def x(self) -> Optional[int | float | Decimal]:
"""The horizontal offset of the button's position. Defaults to ``0``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._x
@x.setter
def x(self, value):
self._x = validators.numeric(value, allow_empty = True)
@property
def y(self) -> Optional[int | float | Decimal]:
"""The vertical offset of the button's position. Defaults to ``0``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._y
@y.setter
def y(self, value):
self._y = validators.numeric(value, allow_empty = True)
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
kwargs = {
'enabled': as_dict.get('enabled', None),
'height': as_dict.get('height', None),
'line_width': as_dict.get('lineWidth', None),
'only_on_hover': as_dict.get('onlyOnHover', None),
'shape': as_dict.get('shape', None),
'style': as_dict.get('style', None),
'width': as_dict.get('width', None),
'x': as_dict.get('x', None),
'y': as_dict.get('y', None),
}
return kwargs
def _to_untrimmed_dict(self, in_cls = None) -> dict:
untrimmed = {
'enabled': self.enabled,
'height': self.height,
'lineWidth': self.line_width,
'onlyOnHover': self.only_on_hover,
'shape': self.shape,
'style': self.style,
'width': self.width,
'x': self.x,
'y': self.y
}
return untrimmed
[docs]class ContextButtonConfiguration(ButtonConfiguration):
"""Configuration options that apply to the Context Menu button."""
def __init__(self, **kwargs):
self._class_name = None
self._menu_class_name = None
self._menu_items = None
self._onclick = None
self._symbol = None
self._symbol_fill = None
self._title_key = None
self._x = None
self.class_name = kwargs.get('class_name', None)
self.menu_class_name = kwargs.get('menu_class_name', None)
self.menu_items = kwargs.get('menu_items', None)
self.onclick = kwargs.get('onclick', None)
self.symbol = kwargs.get('symbol', None)
self.symbol_fill = kwargs.get('symbol_fill', None)
self.title_key = kwargs.get('title_key', None)
self.x = kwargs.get('x', None)
super().__init__(**kwargs)
@property
def class_name(self) -> Optional[str]:
"""The class name of the context button. Defaults to
``'highcharts-contextbutton'``.
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._class_name
@class_name.setter
def class_name(self, value):
self._class_name = validators.string(value, allow_empty = True)
@property
def menu_class_name(self) -> Optional[str]:
"""The class name of the context menu that appears from the button. Defaults to
``'highcharts-contextmenu'``.
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._menu_class_name
@menu_class_name.setter
def menu_class_name(self, value):
self._menu_class_name = validators.string(value, allow_empty = True)
@property
def menu_items(self) -> Optional[List[str]]:
"""A collection of strings pointing to config options for the menu items.
The config options are defined in the :class:`Exporting.menu_item_definitions`
option.
.. note::
By default, the context menu contains "View in fullscreen" and "Print" menu
items, plus one menu item for each of the available export types.
Defaults to:
.. code-block:: python
[
"viewFullscreen",
"printChart",
"separator",
"downloadPNG",
"downloadJPEG",
"downloadPDF",
"downloadSVG"
]
:rtype: :class:`list <python:list>` of :class:`str <python:str>` or
:obj:`None <python:None>`
"""
return self._menu_items
@menu_items.setter
def menu_items(self, value):
if value is None:
self._menu_items = None
else:
if not checkers.is_iterable(value):
raise errors.HighchartsValueError(f'menu_items expects an iterable, but '
f'received: {value.__class__.__name__}')
for item in value:
if not isinstance(item, str):
raise errors.HighchartsValueError(f'specific menu items must be '
f'strings, but received: '
f'{item.__class__.__name}')
self._menu_items = value
@property
def onclick(self) -> Optional[CallbackFunction]:
"""JavaScript event callback function which fires when the button is clicked.
:rtype: :class:`CallbackFunction` or :obj:`None <python:None>`
"""
return self._onclick
@onclick.setter
@class_sensitive(CallbackFunction)
def onclick(self, value):
self._onclick = value
@property
def symbol(self) -> Optional[str]:
"""The symbol to display on the button. Defaults to ``'menu'``.
Points to a definition function in the JavaScript ``Highcharts.Renderer.symbols``
collection.
The default menu function is part of the exporting module. Possible values are:
* ``"circle"``
* ``"square"``
* ``"diamond"``
* ``"triangle"``
* ``"triangle-down"``
* ``"menu"``
* ``"menuball"``
* or a custom shape
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._symbol
@symbol.setter
def symbol(self, value):
self._symbol = validators.string(value, allow_empty = True)
@property
def symbol_fill(self) -> Optional[str]:
"""The color to use for the symbol. Defaults to ``'#666666'``.
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._symbol_fill
@symbol_fill.setter
def symbol_fill(self, value):
self._symbol_fill = validators.string(value, allow_empty = True)
@property
def title_key(self) -> Optional[str]:
"""The key to a :class:`Options.language` option setting that is used for the
button's title tooltip.
When the key is ``'contextButtonTitle'``, it refers to
``language.contextButtonTitle``, whose value defaults to ``"Chart context menu"``.
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._title_key
@title_key.setter
def title_key(self, value):
self._title_key = validators.string(value, allow_empty = True)
@property
def x(self) -> Optional[int | float | Decimal]:
"""The horizontal offset of the button's position relative to its ``align``
setting. Defaults to ``-10``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._x
@x.setter
def x(self, value):
self._x = validators.numeric(value, allow_empty = True)
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
kwargs = {
'class_name': as_dict.get('className', None),
'enabled': as_dict.get('enabled', None),
'menu_class_name': as_dict.get('menuClassName', None),
'menu_items': as_dict.get('menuItems', None),
'onclick': as_dict.get('onclick', None),
'symbol': as_dict.get('symbol', None),
'symbol_fill': as_dict.get('symbolFill', None),
'text': as_dict.get('text', None),
'theme': as_dict.get('theme', None),
'title_key': as_dict.get('titleKey', None),
'x': as_dict.get('x', None),
'y': as_dict.get('y', None),
}
return kwargs
def _to_untrimmed_dict(self, in_cls = None) -> dict:
untrimmed = {
'className': self.class_name,
'enabled': self.enabled,
'menuClassName': self.menu_class_name,
'menuItems': self.menu_items,
'onclick': self.onclick,
'symbol': self.symbol,
'symbolFill': self.symbol_fill,
'text': self.text,
'theme': self.theme,
'titleKey': self.title_key,
'x': self.x,
'y': self.y
}
return untrimmed
class NavigationButtonConfiguration(ButtonConfiguration):
"""Configuration options that apply to the Navigation buttons."""
def __init__(self, **kwargs):
self._align = None
self._button_spacing = None
self._height = None
self._symbol_fill = None
self._symbol_size = None
self._symbol_stroke = None
self._symbol_stroke_width = None
self._symbol_x = None
self._symbol_y = None
self._use_html = None
self._vertical_align = None
self._width = None
self.align = kwargs.get('align', None)
self.button_spacing = kwargs.get('button_spacing', None)
self.height = kwargs.get('height', None)
self.symbol_fill = kwargs.get('symbol_fill', None)
self.symbol_size = kwargs.get('symbol_size', None)
self.symbol_stroke = kwargs.get('symbol_stroke', None)
self.symbol_stroke_width = kwargs.get('symbol_stroke_width', None)
self.symbol_x = kwargs.get('symbol_x', None)
self.symbol_y = kwargs.get('symbol_y', None)
self.use_html = kwargs.get('use_html', None)
self.vertical_align = kwargs.get('vertical_align', None)
self.width = kwargs.get('width', None)
super().__init__(**kwargs)
@property
def align(self) -> Optional[str]:
"""The alignment for the button. Defaults to ``'right'``.
Accepts:
* ``'left'``
* ``'center'``
* ``'right'``
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._align
@align.setter
def align(self, value):
if not value:
self._align = None
else:
value = validators.string(value, allow_empty = False)
value = value.lower()
if value not in ['left', 'center', 'right']:
raise errors.HighchartsValueError(f'align must be either "left", '
f'"center", or "right". Was: {value}')
self._align = value
@property
def button_spacing(self) -> Optional[int | float | Decimal]:
"""The pixel spacing between buttons. Defaults to ``3``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._button_spacing
@button_spacing.setter
def button_spacing(self, value):
self._button_spacing = validators.numeric(value, allow_empty = True)
@property
def height(self) -> Optional[int | float | Decimal]:
"""The height of the buttons in pixels. Defaults to ``28``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._height
@height.setter
def height(self, value):
self._height = validators.numeric(value, allow_empty = True)
@property
def symbol_fill(self) -> Optional[str]:
"""The fill color of the symbol within the buttons. Defaults to ``'#666666'``.
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._symbol_fill
@symbol_fill.setter
def symbol_fill(self, value):
self._symbol_fill = validators.string(value, allow_empty = True)
@property
def symbol_size(self) -> Optional[int | float | Decimal]:
"""The pixel size of the symbol on the buttons. Defaults to ``14``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._symbol_size
@symbol_size.setter
def symbol_size(self, value):
self._symbol_size = validators.numeric(value, allow_empty = True)
@property
def symbol_stroke(self) -> Optional[str]:
"""The stroke color of the symbol's line within the buttons. Defaults to
``'#666666'``.
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._symbol_stroke
@symbol_stroke.setter
def symbol_stroke(self, value):
self._symbol_stroke = validators.string(value, allow_empty = True)
@property
def symbol_stroke_width(self) -> Optional[int | float | Decimal]:
"""The stroke width of the symbol on the buttons, expressed in pixels. Defaults
to ``1``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._symbol_stroke_width
@symbol_stroke_width.setter
def symbol_stroke_width(self, value):
self._symbol_stroke_width = validators.numeric(value, allow_empty = True)
@property
def symbol_x(self) -> Optional[int | float | Decimal]:
"""The x position of the center of the symbol inside the button. Defaults to
``14.5``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._symbol_x
@symbol_x.setter
def symbol_x(self, value):
self._symbol_x = validators.numeric(value, allow_empty = True)
@property
def symbol_y(self) -> Optional[int | float | Decimal]:
"""The y position of the center of the symbol inside the button. Defaults to
``13.5``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._symbol_y
@symbol_y.setter
def symbol_y(self, value):
self._symbol_y = validators.numeric(value, allow_empty = True)
@property
def use_html(self) -> Optional[bool]:
"""Whether to use HTML to render the buttons. Defaults to ``False``.
:rtype: :class:`bool <python:bool>` or :obj:`None <python:None>`
"""
return self._use_html
@use_html.setter
def use_html(self, value):
if value is None:
self._use_html = None
else:
self._use_html = bool(value)
@property
def vertical_align(self) -> Optional[str]:
"""The vertical alignment of the buttons. Defaults to ``'top'``.
Accepts:
* ``'top'``
* ``'middle'``
* ``'bottom'``
:rtype: :class:`str <python:str>` or :obj:`None <python:None>`
"""
return self._vertical_align
@vertical_align.setter
def vertical_align(self, value):
if not value:
self._vertical_align = None
else:
value = validators.string(value, allow_empty = False)
value = value.lower()
if value not in ['top', 'middle', 'bottom']:
raise errors.HighchartsValueError(f'vertical_align must be either "top", '
f'"middle", or "bottom". Was: {value}')
self._vertical_align = value
@property
def width(self) -> Optional[int | float | Decimal]:
"""The width of the button, in pixels. Defaults to ``28``.
:rtype: numeric or :obj:`None <python:None>`
"""
return self._width
@width.setter
def width(self, value):
self._width = validators.numeric(value, allow_empty = True)
@classmethod
def _get_kwargs_from_dict(cls, as_dict):
kwargs = {
'enabled': as_dict.get('enabled', None),
'text': as_dict.get('text', None),
'theme': as_dict.get('theme', None),
'y': as_dict.get('y', None),
'align': as_dict.get('align', None),
'button_spacing': as_dict.get('buttonSpacing', None),
'height': as_dict.get('height', None),
'symbol_fill': as_dict.get('symbolFill', None),
'symbol_size': as_dict.get('symbolSize', None),
'symbol_stroke': as_dict.get('symbolStroke', None),
'symbol_stroke_width': as_dict.get('symbolStrokeWidth', None),
'symbol_x': as_dict.get('symbolX', None),
'symbol_y': as_dict.get('symbolY', None),
'use_html': as_dict.get('useHTML', None),
'vertical_align': as_dict.get('verticalAlign', None),
'width': as_dict.get('width', None),
}
return kwargs
def _to_untrimmed_dict(self, in_cls = None) -> dict:
untrimmed = {
'align': self.align,
'buttonSpacing': self.button_spacing,
'height': self.height,
'symbolFill': self.symbol_fill,
'symbolSize': self.symbol_size,
'symbolStroke': self.symbol_stroke,
'symbolStrokeWidth': self.symbol_stroke_width,
'symbolX': self.symbol_x,
'symbolY': self.symbol_y,
'useHTML': self.use_html,
'verticalAlign': self.vertical_align,
'width': self.width,
}
parent_as_dict = super()._to_untrimmed_dict(in_cls = in_cls) or {}
for key in parent_as_dict:
untrimmed[key] = parent_as_dict[key]
return untrimmed