Spaces:
Running
on
Zero
Running
on
Zero
| #----------------------------------------------------------------------------- | |
| # Copyright (c) 2012 - 2022, Anaconda, Inc., and Bokeh Contributors. | |
| # All rights reserved. | |
| # | |
| # The full license is in the file LICENSE.txt, distributed with this software. | |
| #----------------------------------------------------------------------------- | |
| ''' Represent granular events that can be used to trigger callbacks. | |
| Bokeh documents and applications are capable of supporting various kinds of | |
| interactions. These are often associated with events, such as mouse or touch | |
| events, interactive downsampling mode activation, widget or tool events, and | |
| others. The classes in this module represent these different events, so that | |
| callbacks can be attached and executed when they occur. | |
| It is possible to respond to events with ``CustomJS`` callbacks, which will | |
| function with or without a Bokeh server. This can be accomplished by passing | |
| and event class, and a ``CustomJS`` model to the | |
| :func:`~bokeh.model.Model.js_on_event` method. When the ``CustomJS`` is | |
| executed in the browser, its ``cb_obj`` argument will contain the concrete | |
| event object that triggered the callback. | |
| .. code-block:: python | |
| from bokeh.events import ButtonClick | |
| from bokeh.models import Button, CustomJS | |
| button = Button() | |
| button.js_on_event(ButtonClick, CustomJS(code='console.log("JS:Click")')) | |
| Alternatively it is possible to trigger Python code to run when events | |
| happen, in the context of a Bokeh application running on a Bokeh server. | |
| This can accomplished by passing an event class, and a callback function | |
| to the the :func:`~bokeh.model.Model.on_event` method. The callback should | |
| accept a single argument ``event``, which will be passed the concrete | |
| event object that triggered the callback. | |
| .. code-block:: python | |
| from bokeh.events import ButtonClick | |
| from bokeh.models import Button | |
| button = Button() | |
| def callback(event): | |
| print('Python:Click') | |
| button.on_event(ButtonClick, callback) | |
| .. note :: | |
| There is no throttling of events. Some events such as ``MouseMove`` | |
| may trigger at a very high rate. | |
| ''' | |
| #----------------------------------------------------------------------------- | |
| # Boilerplate | |
| #----------------------------------------------------------------------------- | |
| from __future__ import annotations | |
| import logging # isort:skip | |
| log = logging.getLogger(__name__) | |
| #----------------------------------------------------------------------------- | |
| # Imports | |
| #----------------------------------------------------------------------------- | |
| # Standard library imports | |
| from typing import ( | |
| TYPE_CHECKING, | |
| ClassVar, | |
| Dict, | |
| Type, | |
| ) | |
| # External imports | |
| from typing_extensions import Literal, TypedDict | |
| ## Bokeh imports | |
| if TYPE_CHECKING: | |
| from .core.types import ID, GeometryData, Unknown | |
| from .model import Model | |
| from .models.plots import Plot | |
| from .models.widgets.buttons import AbstractButton | |
| #----------------------------------------------------------------------------- | |
| # Globals and constants | |
| #----------------------------------------------------------------------------- | |
| __all__ = ( | |
| 'ButtonClick', | |
| 'DocumentEvent', | |
| 'DocumentReady', | |
| 'DoubleTap', | |
| 'Event', | |
| 'LODStart', | |
| 'LODEnd', | |
| 'MenuItemClick', | |
| 'ModelEvent', | |
| 'MouseEnter', | |
| 'MouseLeave', | |
| 'MouseMove', | |
| 'MouseWheel', | |
| 'Pan', | |
| 'PanEnd', | |
| 'PanStart', | |
| 'Pinch', | |
| 'PinchEnd', | |
| 'PinchStart', | |
| 'RangesUpdate', | |
| 'Rotate', | |
| 'RotateEnd', | |
| 'RotateStart', | |
| 'PlotEvent', | |
| 'PointEvent', | |
| 'Press', | |
| 'PressUp', | |
| 'Reset', | |
| 'SelectionGeometry', | |
| 'Tap', | |
| ) | |
| #----------------------------------------------------------------------------- | |
| # Private API | |
| #----------------------------------------------------------------------------- | |
| _CONCRETE_EVENT_CLASSES: Dict[str, Type[Event]] = {} | |
| #----------------------------------------------------------------------------- | |
| # General API | |
| #----------------------------------------------------------------------------- | |
| class EventJson(TypedDict): | |
| event_name: str | |
| event_values: Dict[str, Unknown] | |
| class Event: | |
| ''' Base class for all Bokeh events. | |
| This base class is not typically useful to instantiate on its own. | |
| ''' | |
| event_name: ClassVar[str] | |
| def __init_subclass__(cls): | |
| super().__init_subclass__() | |
| if hasattr(cls, "event_name"): | |
| _CONCRETE_EVENT_CLASSES[cls.event_name] = cls | |
| def decode_json(cls, dct: EventJson) -> Event: | |
| ''' Custom JSON decoder for Events. | |
| Can be used as the ``object_hook`` argument of ``json.load`` or | |
| ``json.loads``. | |
| Args: | |
| dct (dict) : a JSON dictionary to decode | |
| The dictionary should have keys ``event_name`` and ``event_values`` | |
| Raises: | |
| ValueError, if the event_name is unknown | |
| Examples: | |
| .. code-block:: python | |
| >>> import json | |
| >>> from bokeh.events import Event | |
| >>> data = '{"event_name": "pan", "event_values" : {"model_id": 1, "x": 10, "y": 20, "sx": 200, "sy": 37}}' | |
| >>> json.loads(data, object_hook=Event.decode_json) | |
| <bokeh.events.Pan object at 0x1040f84a8> | |
| ''' | |
| if not ('event_name' in dct and 'event_values' in dct): | |
| return dct | |
| event_name = dct['event_name'] | |
| if event_name not in _CONCRETE_EVENT_CLASSES: | |
| raise ValueError("Could not find appropriate Event class for event_name: %r" % event_name) | |
| event_values = dct['event_values'] | |
| model_id = event_values.pop('model', {"id": None})["id"] | |
| event_cls = _CONCRETE_EVENT_CLASSES[event_name] | |
| if issubclass(event_cls, ModelEvent): | |
| event = event_cls(model=None, **event_values) | |
| event._model_id = model_id | |
| else: | |
| event = event_cls(**event_values) | |
| return event | |
| class DocumentEvent(Event): | |
| ''' Base class for all Bokeh Document events. | |
| This base class is not typically useful to instantiate on its own. | |
| ''' | |
| class DocumentReady(DocumentEvent): | |
| ''' Announce when a Document is fully idle. | |
| ''' | |
| event_name = 'document_ready' | |
| class ModelEvent(Event): | |
| ''' Base class for all Bokeh Model events. | |
| This base class is not typically useful to instantiate on its own. | |
| ''' | |
| _model_id: ID | None | |
| def __init__(self, model: Model | None) -> None: | |
| ''' Create a new base event. | |
| Args: | |
| model (Model) : a Bokeh model to register event callbacks on | |
| ''' | |
| self._model_id = None | |
| if model is not None: | |
| self._model_id = model.id | |
| class ButtonClick(ModelEvent): | |
| ''' Announce a button click event on a Bokeh button widget. | |
| ''' | |
| event_name = 'button_click' | |
| def __init__(self, model: AbstractButton | None) -> None: | |
| from .models.widgets import AbstractButton | |
| if model is not None and not isinstance(model, AbstractButton): | |
| clsname = self.__class__.__name__ | |
| raise ValueError(f"{clsname} event only applies to button models") | |
| super().__init__(model=model) | |
| class MenuItemClick(ModelEvent): | |
| ''' Announce a button click event on a Bokeh menu item. | |
| ''' | |
| event_name = 'menu_item_click' | |
| def __init__(self, model: Model, item: str | None = None) -> None: | |
| self.item = item | |
| super().__init__(model=model) | |
| class PlotEvent(ModelEvent): | |
| ''' The base class for all events applicable to Plot models. | |
| ''' | |
| def __init__(self, model: Plot | None) -> None: | |
| from .models import Plot | |
| if model is not None and not isinstance(model, Plot): | |
| raise ValueError(f"{self.__class__.__name__} event only applies to Plot models") | |
| super().__init__(model) | |
| class LODStart(PlotEvent): | |
| ''' Announce the start of "interactive level-of-detail" mode on a plot. | |
| During interactive actions such as panning or zooming, Bokeh can | |
| optionally, temporarily draw a reduced set of the data, in order to | |
| maintain high interactive rates. This is referred to as interactive | |
| Level-of-Detail (LOD) mode. This event fires whenever a LOD mode | |
| has just begun. | |
| ''' | |
| event_name = 'lodstart' | |
| class LODEnd(PlotEvent): | |
| ''' Announce the end of "interactive level-of-detail" mode on a plot. | |
| During interactive actions such as panning or zooming, Bokeh can | |
| optionally, temporarily draw a reduced set of the data, in order to | |
| maintain high interactive rates. This is referred to as interactive | |
| Level-of-Detail (LOD) mode. This event fires whenever a LOD mode | |
| has just ended. | |
| ''' | |
| event_name = 'lodend' | |
| class RangesUpdate(PlotEvent): | |
| ''' Announce combined range updates in a single event. | |
| Attributes: | |
| x0 (float) : start x-coordinate for the default x-range | |
| x1 (float) : end x-coordinate for the default x-range | |
| y0 (float) : start x-coordinate for the default y-range | |
| y1 (float) : end y-coordinate for the default x-range | |
| Callbacks may be added to range ``start`` and ``end`` properties to respond | |
| to range changes, but this can result in multiple callbacks being invoked | |
| for a single logical operation (e.g. a pan or zoom). This event is emitted | |
| by supported tools when the entire range update is complete, in order to | |
| afford a *single* event that can be responded to. | |
| ''' | |
| event_name = 'rangesupdate' | |
| def __init__(self, model: Plot | None, *, | |
| x0: float | None = None, | |
| x1: float | None = None, | |
| y0: float | None = None, | |
| y1: float | None = None): | |
| self.x0 = x0 | |
| self.x1 = x1 | |
| self.y0 = y0 | |
| self.y1 = y1 | |
| super().__init__(model=model) | |
| class SelectionGeometry(PlotEvent): | |
| ''' Announce the coordinates of a selection event on a plot. | |
| Attributes: | |
| geometry (dict) : a dictionary containing the coordinates of the | |
| selection event. | |
| final (bool) : whether the selection event is the last selection event | |
| in the case of selections on every mousemove. | |
| ''' | |
| event_name = "selectiongeometry" | |
| def __init__(self, model: Plot | None, geometry: GeometryData | None = None, final: bool = True) -> None: | |
| self.geometry = geometry | |
| self.final = final | |
| super().__init__(model=model) | |
| class Reset(PlotEvent): | |
| ''' Announce a button click event on a plot ``ResetTool``. | |
| ''' | |
| event_name = "reset" | |
| def __init__(self, model: Plot | None) -> None: | |
| super().__init__(model=model) | |
| class PointEvent(PlotEvent): | |
| ''' Base class for UI events associated with a specific (x,y) point. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| Note that data space coordinates are relative to the default range, not | |
| any extra ranges, and the the screen space origin is at the top left of | |
| the HTML canvas. | |
| ''' | |
| def __init__(self, model: Plot | None, sx: float | None = None, sy: | |
| float | None = None, x: float | None = None, y: float | None = None): | |
| self.sx = sx | |
| self.sy = sy | |
| self.x = x | |
| self.y = y | |
| super().__init__(model=model) | |
| class Tap(PointEvent): | |
| ''' Announce a tap or click event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| ''' | |
| event_name = 'tap' | |
| class DoubleTap(PointEvent): | |
| ''' Announce a double-tap or double-click event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| ''' | |
| event_name = 'doubletap' | |
| class Press(PointEvent): | |
| ''' Announce a press event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| ''' | |
| event_name = 'press' | |
| class PressUp(PointEvent): | |
| ''' Announce a pressup event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| ''' | |
| event_name = 'pressup' | |
| class MouseEnter(PointEvent): | |
| ''' Announce a mouse enter event onto a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| The enter event is generated when the mouse leaves the entire Plot | |
| canvas, including any border padding and space for axes or legends. | |
| ''' | |
| event_name = 'mouseenter' | |
| class MouseLeave(PointEvent): | |
| ''' Announce a mouse leave event from a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| The leave event is generated when the mouse leaves the entire Plot | |
| canvas, including any border padding and space for axes or legends. | |
| ''' | |
| event_name = 'mouseleave' | |
| class MouseMove(PointEvent): | |
| ''' Announce a mouse movement event over a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| This event can fire at a very high rate, potentially increasing network | |
| traffic or CPU load. | |
| ''' | |
| event_name = 'mousemove' | |
| class MouseWheel(PointEvent): | |
| ''' Announce a mouse wheel event on a Bokeh plot. | |
| Attributes: | |
| delta (float) : the (signed) scroll speed | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| By default, Bokeh plots do not prevent default scroll events unless a | |
| ``WheelZoomTool`` or ``WheelPanTool`` is active. This may change in | |
| future releases. | |
| ''' | |
| event_name = 'wheel' | |
| def __init__(self, | |
| model: Plot | None, | |
| *, | |
| delta: float | None = None, | |
| sx: float | None = None, | |
| sy: float | None = None, | |
| x: float | None = None, | |
| y: float | None = None): | |
| self.delta = delta | |
| super().__init__(model, sx=sx, sy=sy, x=x, y=y) | |
| class Pan(PointEvent): | |
| ''' Announce a pan event on a Bokeh plot. | |
| Attributes: | |
| delta_x (float) : the amount of scroll in the x direction | |
| delta_y (float) : the amount of scroll in the y direction | |
| direction (float) : the direction of scroll (1 or -1) | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| ''' | |
| event_name = 'pan' | |
| def __init__(self, | |
| model: Plot | None, | |
| *, | |
| delta_x: float | None = None, | |
| delta_y: float | None = None, | |
| direction: Literal[-1, -1] | None = None, | |
| sx: float | None = None, | |
| sy: float | None = None, | |
| x: float | None = None, | |
| y: float | None = None): | |
| self.delta_x = delta_x | |
| self.delta_y = delta_y | |
| self.direction = direction | |
| super().__init__(model, sx=sx, sy=sy, x=x, y=y) | |
| class PanEnd(PointEvent): | |
| ''' Announce the end of a pan event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| ''' | |
| event_name = 'panend' | |
| class PanStart(PointEvent): | |
| ''' Announce the start of a pan event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| ''' | |
| event_name = 'panstart' | |
| class Pinch(PointEvent): | |
| ''' Announce a pinch event on a Bokeh plot. | |
| Attributes: | |
| scale (float) : the (signed) amount of scaling | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| This event is only applicable for touch-enabled devices. | |
| ''' | |
| event_name = 'pinch' | |
| def __init__(self, | |
| model: Plot | None, | |
| *, | |
| scale: float | None = None, | |
| sx: float | None = None, | |
| sy: float | None = None, | |
| x: float | None = None, | |
| y: float | None = None): | |
| self.scale = scale | |
| super().__init__(model, sx=sx, sy=sy, x=x, y=y) | |
| class PinchEnd(PointEvent): | |
| ''' Announce the end of a pinch event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| This event is only applicable for touch-enabled devices. | |
| ''' | |
| event_name = 'pinchend' | |
| class PinchStart(PointEvent): | |
| ''' Announce the start of a pinch event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| This event is only applicable for touch-enabled devices. | |
| ''' | |
| event_name = 'pinchstart' | |
| class Rotate(PointEvent): | |
| ''' Announce a rotate event on a Bokeh plot. | |
| Attributes: | |
| rotation (float) : the rotation that has been done (in deg) | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| This event is only applicable for touch-enabled devices. | |
| ''' | |
| event_name = 'rotate' | |
| def __init__(self, | |
| model: Plot | None, | |
| *, | |
| rotation: float | None = None, | |
| sx: float | None = None, | |
| sy: float | None = None, | |
| x: float | None = None, | |
| y: float | None = None): | |
| self.rotation = rotation | |
| super().__init__(model, sx=sx, sy=sy, x=x, y=y) | |
| class RotateEnd(PointEvent): | |
| ''' Announce the end of a rotate event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| This event is only applicable for touch-enabled devices. | |
| ''' | |
| event_name = 'rotateend' | |
| class RotateStart(PointEvent): | |
| ''' Announce the start of a rotate event on a Bokeh plot. | |
| Attributes: | |
| sx (float) : x-coordinate of the event in *screen* space | |
| sy (float) : y-coordinate of the event in *screen* space | |
| x (float) : x-coordinate of the event in *data* space | |
| y (float) : y-coordinate of the event in *data* space | |
| .. note:: | |
| This event is only applicable for touch-enabled devices. | |
| ''' | |
| event_name = 'rotatestart' | |
| #----------------------------------------------------------------------------- | |
| # Dev API | |
| #----------------------------------------------------------------------------- | |
| #----------------------------------------------------------------------------- | |
| # Code | |
| #----------------------------------------------------------------------------- | |