CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-bokeh

Interactive plots and applications in the browser from Python

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

events-interactivity.mddocs/

Events and Interactivity

Event system for building interactive applications and handling user interactions. Provides 30+ event types for mouse interactions, keyboard input, plot events, and custom events. Essential for creating responsive visualizations and interactive dashboards.

Capabilities

Base Event Classes

Core event classes that form the foundation of Bokeh's event system.

class Event:
    """
    Base class for all Bokeh events.
    
    All events inherit from this class and provide common properties.
    """
    model: Model      # The model that generated the event
    event_name: str   # Name of the event type

class DocumentEvent(Event):
    """
    Document-level events.
    
    Events that occur at the document level, affecting the entire application.
    """
    document: Document  # The document where the event occurred

class ModelEvent(Event):
    """
    Model-level events.
    
    Events that occur on specific model objects.
    """

class PlotEvent(Event):
    """
    Plot-specific events.
    
    Events that occur within plot areas.
    """

class PointEvent(PlotEvent):
    """
    Events with coordinate information.
    
    Events that have associated x/y coordinates in data space.
    """
    x: float  # X coordinate in data space
    y: float  # Y coordinate in data space
    sx: int   # X coordinate in screen space (pixels)
    sy: int   # Y coordinate in screen space (pixels)

Mouse Events

Events triggered by mouse interactions within plots.

class Tap(PointEvent):
    """Single mouse click/tap event."""

class DoubleTap(PointEvent):
    """Double mouse click/tap event."""

class Press(PointEvent):
    """Mouse button press event."""

class PressUp(PointEvent):
    """Mouse button release event."""

class MouseEnter(PointEvent):
    """Mouse cursor enters plot area."""

class MouseLeave(PointEvent):
    """Mouse cursor leaves plot area."""

class MouseMove(PointEvent):
    """Mouse cursor movement within plot."""

class MouseWheel(PointEvent):
    """Mouse wheel scroll event."""
    delta: float  # Scroll delta value

Pan and Zoom Events

Events related to plot navigation and viewport changes.

class Pan(PointEvent):
    """Pan/drag event during movement."""
    direction: int  # Pan direction

class PanStart(PointEvent):
    """Pan/drag start event."""

class PanEnd(PointEvent):
    """Pan/drag end event."""

class Pinch(PointEvent):
    """Pinch/zoom gesture event."""
    scale: float  # Zoom scale factor

class PinchStart(PointEvent):
    """Pinch/zoom gesture start event."""

class PinchEnd(PointEvent):
    """Pinch/zoom gesture end event."""

class MouseWheel(PointEvent):
    """Mouse wheel zoom event."""
    delta: float  # Wheel delta value

Selection Events

Events related to data selection and highlighting.

class SelectionGeometry(PlotEvent):
    """Selection geometry change event."""
    geometry: Dict[str, Any]  # Selection geometry specification
    final: bool               # Whether selection is finalized

class Reset(PlotEvent):
    """Plot reset event (clear selections, reset view)."""

class RangesUpdate(PlotEvent):
    """Plot ranges update event."""
    x0: float  # New x-range start
    x1: float  # New x-range end  
    y0: float  # New y-range start
    y1: float  # New y-range end

UI Component Events

Events from interactive UI components like buttons and widgets.

class ButtonClick(ModelEvent):
    """Button click event."""

class MenuItemClick(ModelEvent):
    """Menu item selection event."""
    item: str  # Selected menu item value

class ValueSubmit(ModelEvent):
    """Value submission event (from input widgets)."""
    value: Any  # Submitted value

class LegendItemClick(ModelEvent):
    """Legend item click event."""
    item: str  # Legend item identifier

Plot-Specific Events

Events related to plot rendering and lifecycle.

class LODStart(PlotEvent):
    """Level-of-detail rendering start event."""

class LODEnd(PlotEvent):
    """Level-of-detail rendering end event."""

class AxisClick(PlotEvent):
    """Axis label or tick click event."""
    axis: Axis  # The clicked axis object

Connection and Lifecycle Events

Events related to client-server connection and document lifecycle.

class DocumentReady(DocumentEvent):
    """Document fully loaded and ready event."""

class ConnectionLost(DocumentEvent):
    """Server connection lost event."""

class ClientReconnected(DocumentEvent):
    """Client reconnected to server event."""

Touch and Gesture Events

Events for touch-based interactions on mobile devices.

class Rotate(PointEvent):
    """Rotation gesture event."""
    rotation: float  # Rotation angle in radians

class RotateStart(PointEvent):
    """Rotation gesture start event."""

class RotateEnd(PointEvent):
    """Rotation gesture end event."""

Event Handling

Methods for registering event callbacks and handling events.

# Event callback registration (available on all models)
def on_event(self, event_type, *callbacks):
    """
    Register callbacks for specific event types.
    
    Parameters:
    - event_type: Event class or event name string
    - callbacks: Callback functions to register
    
    Each callback receives the event object as its argument.
    """

# Example callback signature
def event_callback(event: Event) -> None:
    """
    Event callback function.
    
    Parameters:
    - event: The event object containing event data
    """

# JavaScript callbacks for client-side handling
class CustomJS:
    """JavaScript callback for client-side event handling."""
    def __init__(self, args=None, code=""):
        """
        Parameters:
        - args: Dictionary of Python objects available in JavaScript
        - code: JavaScript code string to execute
        """
        
    args: Dict[str, Any]  # Python objects available as JavaScript variables
    code: str             # JavaScript code to execute

Usage Examples

Basic Event Handling

from bokeh.plotting import figure, show, curdoc
from bokeh.events import Tap
from bokeh.models import ColumnDataSource
import numpy as np

# Create data and plot
x = np.random.random(100)
y = np.random.random(100)
source = ColumnDataSource(data=dict(x=x, y=y))

p = figure(width=400, height=400, tools="tap", title="Click on points")
p.circle('x', 'y', source=source, size=10, alpha=0.6)

def tap_handler(event):
    """Handle tap events."""
    print(f"Tapped at: ({event.x:.2f}, {event.y:.2f})")
    
# Register event handler
p.on_event(Tap, tap_handler)

# For server applications
curdoc().add_root(p)

# For standalone scripts
# show(p)

Selection Event Handling

from bokeh.plotting import figure, curdoc
from bokeh.events import SelectionGeometry
from bokeh.models import ColumnDataSource
import numpy as np

# Create data
n = 300
x = np.random.random(n)
y = np.random.random(n)
colors = np.random.choice(['red', 'green', 'blue'], n)

source = ColumnDataSource(data=dict(x=x, y=y, colors=colors))

p = figure(width=500, height=500, tools="box_select,lasso_select,reset", 
           title="Select points to see coordinates")
p.circle('x', 'y', source=source, color='colors', size=8, alpha=0.6)

def selection_handler(event):
    """Handle selection events."""
    indices = source.selected.indices
    print(f"Selected {len(indices)} points")
    if indices:
        selected_x = [source.data['x'][i] for i in indices]
        selected_y = [source.data['y'][i] for i in indices]
        print(f"X range: {min(selected_x):.2f} - {max(selected_x):.2f}")
        print(f"Y range: {min(selected_y):.2f} - {max(selected_y):.2f}")

p.on_event(SelectionGeometry, selection_handler)

curdoc().add_root(p)

Mouse Movement Tracking

from bokeh.plotting import figure, curdoc
from bokeh.events import MouseMove
from bokeh.models import Div, Column
import numpy as np

# Create plot
x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)

p = figure(width=500, height=300, title="Mouse Movement Tracker")
p.line(x, y, line_width=2)

# Create info display
info = Div(text="<p>Move mouse over plot</p>", width=500)

def mouse_handler(event):
    """Handle mouse movement."""
    info.text = f"""
    <p><b>Mouse Position:</b></p>
    <p>Data coordinates: ({event.x:.3f}, {event.y:.3f})</p>
    <p>Screen coordinates: ({event.sx}, {event.sy})</p>
    """

p.on_event(MouseMove, mouse_handler)

layout = Column(p, info)
curdoc().add_root(layout)

Button Click Events

from bokeh.plotting import figure, curdoc
from bokeh.models import Button, ColumnDataSource, Column
from bokeh.events import ButtonClick
import numpy as np

# Create plot with dynamic data
source = ColumnDataSource(data=dict(x=[1, 2, 3], y=[1, 4, 2]))

p = figure(width=400, height=300, title="Dynamic Data")
line = p.line('x', 'y', source=source, line_width=3)

# Create control buttons
update_button = Button(label="Update Data", button_type="success")
reset_button = Button(label="Reset Data", button_type="primary")

def update_data():
    """Generate new random data."""
    n = np.random.randint(5, 15)
    new_data = dict(
        x=sorted(np.random.random(n) * 10),
        y=np.random.random(n) * 10
    )
    source.data = new_data

def reset_data():
    """Reset to original data."""
    source.data = dict(x=[1, 2, 3], y=[1, 4, 2])

# Handle button clicks
def button_handler(event):
    if event.model == update_button:
        update_data()
    elif event.model == reset_button:
        reset_data()

update_button.on_event(ButtonClick, button_handler)
reset_button.on_event(ButtonClick, button_handler)

layout = Column(p, update_button, reset_button)
curdoc().add_root(layout)

JavaScript Callbacks for Client-Side Events

from bokeh.plotting import figure, show
from bokeh.models import CustomJS, ColumnDataSource, Slider, Column
from bokeh.events import MouseMove
import numpy as np

# Create data
x = np.linspace(0, 4*np.pi, 100)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))

# Create plot
p = figure(width=500, height=300, title="Client-Side Interaction")
line = p.line('x', 'y', source=source, line_width=2)

# JavaScript callback for mouse events
mouse_callback = CustomJS(args=dict(p=p), code="""
    // This runs in the browser without server communication
    console.log('Mouse at:', cb_obj.x, cb_obj.y);
    
    // Update plot title with coordinates
    p.title.text = 'Mouse at: (' + cb_obj.x.toFixed(2) + ', ' + cb_obj.y.toFixed(2) + ')';
""")

p.js_on_event('mousemove', mouse_callback)

# Slider with JavaScript callback
slider = Slider(start=0, end=2, value=1, step=0.1, title="Frequency")

slider_callback = CustomJS(args=dict(source=source), code="""
    const data = source.data;
    const f = cb_obj.value;
    const x = data['x'];
    const y = data['y'];
    
    for (let i = 0; i < x.length; i++) {
        y[i] = Math.sin(f * x[i]);
    }
    
    source.change.emit();
""")

slider.js_on_change('value', slider_callback)

layout = Column(p, slider)
show(layout)

Range Update Events

from bokeh.plotting import figure, curdoc
from bokeh.events import RangesUpdate
from bokeh.models import Div, Column
import numpy as np

# Create plot with pan/zoom tools
x = np.random.random(1000)
y = np.random.random(1000)

p = figure(width=500, height=400, tools="pan,wheel_zoom,box_zoom,reset",
           title="Pan and zoom to see range updates")
p.circle(x, y, size=5, alpha=0.5)

# Info display
info = Div(text="<p>Pan or zoom to see range updates</p>", width=500)

def range_handler(event):
    """Handle range update events."""
    info.text = f"""
    <p><b>Current View Ranges:</b></p>
    <p>X: {event.x0:.3f} to {event.x1:.3f}</p>  
    <p>Y: {event.y0:.3f} to {event.y1:.3f}</p>
    <p>Area: {(event.x1-event.x0)*(event.y1-event.y0):.6f}</p>
    """

p.on_event(RangesUpdate, range_handler)

layout = Column(p, info)
curdoc().add_root(layout)

Multi-Event Handler

from bokeh.plotting import figure, curdoc
from bokeh.events import Tap, DoubleTap, Pan, MouseWheel
from bokeh.models import Div, Column
import numpy as np

# Create interactive plot
x = np.random.random(200)
y = np.random.random(200)

p = figure(width=500, height=400, tools="pan,tap", 
           title="Multi-Event Demo")
circles = p.circle(x, y, size=8, alpha=0.6, color='blue')

# Event log
log = Div(text="<p>Event log:</p>", width=500, height=200)

def event_logger(event):
    """Log different types of events."""
    event_type = type(event).__name__
    
    if hasattr(event, 'x') and hasattr(event, 'y'):
        message = f"{event_type} at ({event.x:.2f}, {event.y:.2f})"
    else:
        message = f"{event_type} event"
    
    # Add to log (keep last 10 entries)
    lines = log.text.split('<br>')
    if len(lines) > 10:
        lines = lines[-9:]  # Keep last 9 + new one = 10
    
    lines.append(message)
    log.text = '<br>'.join(lines)

# Register multiple event types
p.on_event(Tap, event_logger)
p.on_event(DoubleTap, event_logger)
p.on_event(Pan, event_logger)
p.on_event(MouseWheel, event_logger)

layout = Column(p, log)
curdoc().add_root(layout)

Install with Tessl CLI

npx tessl i tessl/pypi-bokeh

docs

client-server.md

colors-transforms.md

command-line.md

document-management.md

embedding-integration.md

events-interactivity.md

index.md

io-operations.md

layouts.md

models-data-sources.md

plotting-interface.md

server-applications.md

widgets.md

tile.json