Source code for highcharts_core.utility_classes.buttons

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


[docs]class ButtonTheme(AttributeObject): """Settings used to style buttons.""" def __init__(self, **kwargs): trimmed_kwargs = {x: y for x, y in kwargs.items() if not hasattr(self, x)} super().__init__(**trimmed_kwargs) self.fill = kwargs.get('fill', None) self.padding = kwargs.get('padding', None) self.stroke = kwargs.get('stroke', None) self.states = kwargs.get('states', None) def __setitem__(self, key, item): validate_key = False try: validate_key = key not in self except AttributeError: validate_key = True if validate_key: try: key = validators.variable_name(key, allow_empty = False) except validator_errors.InvalidVariableNameError as error: if '-' in key: try: test_key = key.replace('-', '_') validators.variable_name(test_key, allow_empty = False) except validator_errors.InvalidVariableNameError: raise error else: raise error if self._valid_value_types: try: item = validate_types(item, types = self._valid_value_types, allow_none = self._allow_empty_value) except errors.HighchartsValueError as error: if self._allow_empty_value and not item: item = None else: try: item = self._valid_value_types(item) except (TypeError, ValueError, AttributeError): raise error super().__setitem__(key, item) @property def fill(self) -> Optional[str | Gradient | Pattern]: """The color of the button's fill. The default fill exists only to capture hover events. :rtype: :class:`str <python:str>` (for colors), :class:`Gradient` for gradients, :class:`Pattern` for pattern definitions, or :obj:`None <python:None>` """ return self.get('fill', None) @fill.setter def fill(self, value): self['fill'] = utility_functions.validate_color(value) @property def padding(self) -> Optional[int | float | Decimal]: """Padding for the button. Defaults to `5`. :rtype: numeric """ return self.get('padding', None) @padding.setter def padding(self, value): self['padding'] = validators.numeric(value, allow_empty = True) @property def states(self) -> Optional[States]: """States to apply to the button. Defaults to :obj:`None <python:None>`. :rtype: :class:`States <highcharts_core.utility_classes.states.States>` or :obj:`None <python:None>` """ return self.get('states', None) @states.setter def states(self, value): self['states'] = validate_types(value, States) @property def stroke(self) -> Optional[str]: """The color of the button's stroke. Defaults to ``'none'``. :rtype: :class:`str <python:str>` or :obj:`None <python:None>` """ return self.get('stroke', None) @stroke.setter def stroke(self, value): self['stroke'] = validators.string(value, allow_empty = True) @classmethod def _get_kwargs_from_dict(cls, as_dict): kwargs = { 'fill': as_dict.get('fill', None), 'padding': as_dict.get('padding', None), 'states': as_dict.get('states', None), 'stroke': as_dict.get('stroke', None) } for key in as_dict: if key not in kwargs: kwargs[key] = as_dict.get(key, None) return kwargs
[docs]class ButtonConfiguration(HighchartsMeta): """Configuration of options that apply to a given button.""" def __init__(self, **kwargs): self._enabled = None self._text = None self._theme = None self._y = None self.enabled = kwargs.get('enabled', None) self.text = kwargs.get('text', None) self.theme = kwargs.get('theme', 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 but JavaScript API methods may still be available. 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 text(self) -> Optional[str]: """The text to display on the button. :rtype: :class:`str <python:str>` or :obj:`None <python:None>` """ return self._text @text.setter def text(self, value): if isinstance(value, constants.EnforcedNullType): self._text = None else: self._text = validators.string(value, allow_empty = True) @property def theme(self) -> Optional[ButtonTheme]: """Configuration of the theme and styling to apply to the button. :rtype: :class:`ButtonTheme` or :obj:`None <python:None>` """ return self._theme @theme.setter @class_sensitive(ButtonTheme) def theme(self, value): self._theme = value @property def y(self) -> Optional[int | float | Decimal]: """The vertical offset of the button's position relative to its ``verticalAlign`` setting. 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), 'text': as_dict.get('text', None), 'theme': as_dict.get('theme', None), 'y': as_dict.get('y', None) } return kwargs def _to_untrimmed_dict(self, in_cls = None) -> dict: untrimmed = { 'enabled': self.enabled, 'text': self.text, 'theme': self.theme, 'y': self.y } return untrimmed
[docs]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
[docs]class ExportingButtons(JavaScriptDict): """Special :class:`dict <python:dict>` class used to construct a collection of Highcharts button configurations. Each key represents the identifier of a button, while the object is a configuration of that button's settings. Keys are validated to be valid variable names, while each value is validated to be a :class:`ButtonConfiguration`. """ _valid_value_types = ContextButtonConfiguration _allow_empty_value = True def __init__(self, **kwargs): context_button = kwargs.pop('context_button', None) or kwargs.pop('contextButton', None) if isinstance(context_button, constants.EnforcedNullType): context_button = None elif not context_button: context_button = ContextButtonConfiguration() elif isinstance(context_button, ButtonConfiguration): pass elif isinstance(context_button, dict): context_button = ContextButtonConfiguration.from_dict(context_button) elif isinstance(context_button, str): context_button = ContextButtonConfiguration.from_json(context_button) super().__init__(**kwargs) self['contextButton'] = context_button def __setitem__(self, key, item): if key == 'context_button': key = 'contextButton' validate_key = False try: validate_key = key not in self except AttributeError: validate_key = True if validate_key: try: key = validators.variable_name(key, allow_empty = False) except validator_errors.InvalidVariableNameError as error: if '-' in key: try: test_key = key.replace('-', '_') validators.variable_name(test_key, allow_empty = False) except validator_errors.InvalidVariableNameError: raise error else: raise error if self._valid_value_types: try: item = validate_types(item, types = self._valid_value_types, allow_none = self._allow_empty_value) except errors.HighchartsValueError as error: if self._allow_empty_value and not item: item = None else: try: item = self._valid_value_types(item) except (TypeError, ValueError, AttributeError): raise error super().__setitem__(key, item) def __getitem__(self, key): if key == 'context_button': key = 'contextButton' return super().__getitem__(key)
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