Source code for panel.widgets.button

"""
Defines the Button and button-like widgets which allow triggering
events or merely toggling between on-off states.
"""
from __future__ import annotations

from typing import (
    TYPE_CHECKING, Any, Callable, ClassVar, Dict, List, Mapping, Optional,
    Type,
)

import param

from bokeh.events import ButtonClick, MenuItemClick
from bokeh.models import (
    Button as _BkButton, Dropdown as _BkDropdown, Toggle as _BkToggle,
)
from bokeh.models.ui import SVGIcon, TablerIcon

from ..io.resources import CDN_DIST
from ..links import Callback
from .base import Widget

if TYPE_CHECKING:
    from bokeh.document import Document
    from bokeh.model import Model
    from pyviz_comms import Comm

    from ..links import JSLinkTarget, Link


BUTTON_TYPES: List[str] = ['default', 'primary', 'success', 'warning', 'danger', 'light']
BUTTON_STYLES: List[str] = ['solid', 'outline']

class _ButtonBase(Widget):

    button_type = param.ObjectSelector(default='default', objects=BUTTON_TYPES, doc="""
        A button theme; should be one of 'default' (white), 'primary'
        (blue), 'success' (green), 'info' (yellow), 'light' (light),
        or 'danger' (red).""")

    button_style = param.ObjectSelector(default='solid', objects=BUTTON_STYLES, doc="""
        A button style to switch between 'solid', 'outline'.""")

    _rename: ClassVar[Mapping[str, str | None]] = {'name': 'label', 'button_style': None}

    _source_transforms: ClassVar[Mapping[str, str | None]] = {'button_style': None}

    _stylesheets: ClassVar[List[str]] = [f'{CDN_DIST}css/button.css']

    __abstract = True

    def _process_param_change(self, params):
        if 'button_style' in params or 'css_classes' in params:
            params['css_classes'] = [
                params.pop('button_style', self.button_style)
            ] + params.get('css_classes', self.css_classes)
        return super()._process_param_change(params)


[docs]class IconMixin(Widget): icon = param.String(default=None, doc=""" An icon to render to the left of the button label. Either an SVG or an icon name which is loaded from https://tabler-icons.io/.""") icon_size = param.String(default='1em', doc=""" Size of the icon as a string, e.g. 12px or 1em.""") _rename: ClassVar[Mapping[str, str | None]] = { 'icon_size': None, '_icon': 'icon', 'icon': None } __abstract = True def __init__(self, **params) -> None: self._rename = dict(self._rename, **IconMixin._rename) super().__init__(**params) def _process_param_change(self, params): icon_size = params.pop('icon_size', self.icon_size) if params.get('icon') is not None: icon = params['icon'] if icon.lstrip().startswith('<svg'): icon_model = SVGIcon(svg=icon, size=icon_size) else: icon_model = TablerIcon(icon_name=icon, size=icon_size) params['_icon'] = icon_model return super()._process_param_change(params)
class _ClickButton(_ButtonBase, IconMixin): __abstract = True _event: ClassVar[str] = 'button_click' _source_transforms: ClassVar[Mapping[str, str | None]] = { 'button_style': None, } def _get_model( self, doc: Document, root: Optional[Model] = None, parent: Optional[Model] = None, comm: Optional[Comm] = None ) -> Model: model = super()._get_model(doc, root, parent, comm) self._register_events(self._event, model=model, doc=doc, comm=comm) return model def js_on_click(self, args: Dict[str, Any] = {}, code: str = "") -> Callback: """ Allows defining a JS callback to be triggered when the button is clicked. Arguments ---------- args: dict A mapping of objects to make available to the JS callback code: str The Javascript code to execute when the button is clicked. Returns ------- callback: Callback The Callback which can be used to disable the callback. """ from ..links import Callback return Callback(self, code={'event:'+self._event: code}, args=args) def jscallback(self, args: Dict[str, Any] = {}, **callbacks: str) -> Callback: """ Allows defining a Javascript (JS) callback to be triggered when a property changes on the source object. The keyword arguments define the properties that trigger a callback and the JS code that gets executed. Arguments ---------- args: dict A mapping of objects to make available to the JS callback **callbacks: dict A mapping between properties on the source model and the code to execute when that property changes Returns ------- callback: Callback The Callback which can be used to disable the callback. """ for k, v in list(callbacks.items()): if k == 'clicks': k = 'event:'+self._event val = self._rename.get(v, v) if val is not None: callbacks[k] = val return Callback(self, code=callbacks, args=args)
[docs]class Button(_ClickButton): """ The `Button` widget allows triggering events when the button is clicked. The Button provides a `value` parameter, which will toggle from `False` to `True` while the click event is being processed It also provides an additional `clicks` parameter, that can be watched to subscribe to click events. Reference: https://panel.holoviz.org/reference/widgets/Button.html#widgets-gallery-button :Example: >>> pn.widgets.Button(name='Click me', button_type='primary') """ clicks = param.Integer(default=0, doc=""" Number of clicks (can be listened to)""") value = param.Event(doc=""" Toggles from False to True while the event is being processed.""") _rename: ClassVar[Mapping[str, str | None]] = { 'clicks': None, 'name': 'label', 'value': None } _target_transforms: ClassVar[Mapping[str, str | None]] = { 'event:button_click': None, 'value': None } _widget_type: ClassVar[Type[Model]] = _BkButton def __init__(self, **params): click_handler = params.pop('on_click', None) super().__init__(**params) if click_handler: self.on_click(click_handler) @property def _linkable_params(self) -> List[str]: return super()._linkable_params + ['value'] def _process_event(self, event: param.parameterized.Event) -> None: self.param.trigger('value') self.clicks += 1
[docs] def on_click( self, callback: Callable[[param.parameterized.Event], None] ) -> param.parameterized.Watcher: """ Register a callback to be executed when the `Button` is clicked. The callback is given an `Event` argument declaring the number of clicks Arguments --------- callback: (Callable[[param.parameterized.Event], None]) The function to run on click events. Must accept a positional `Event` argument Returns ------- watcher: param.Parameterized.Watcher A `Watcher` that executes the callback when the button is clicked. """ return self.param.watch(callback, 'clicks', onlychanged=False)
[docs]class Toggle(_ButtonBase, IconMixin): """The `Toggle` widget allows toggling a single condition between `True`/`False` states. This widget is interchangeable with the `Checkbox` widget. Reference: https://panel.holoviz.org/reference/widgets/Toggle.html :Example: >>> Toggle(name='Toggle', button_type='success') """ value = param.Boolean(default=False, doc=""" Whether the button is currently toggled.""") _rename: ClassVar[Mapping[str, str | None]] = { 'value': 'active', 'name': 'label', } _supports_embed: ClassVar[bool] = True _widget_type: ClassVar[Type[Model]] = _BkToggle def _get_embed_state(self, root, values=None, max_opts=3): return (self, self._models[root.ref['id']][0], [False, True], lambda x: x.active, 'active', 'cb_obj.active')