Skip to main content
v2.2

Event Listeners

Your charts and series support tracking user interactions. Register Python callbacks with add_event_listener(...) to react to clicks, pointer moves, layout/resize, cursor hits, and more.

Important: open charts in live mode so events are streamed back to Python.

chart.open(live=True)

Live display mode is enabled with open(live=True).

Quick start: explore the payloads

The fastest way to learn “what does this event return here?” is to print the event dict.

def probe(ev: dict):
print(ev)

# Try a few:
chart.add_event_listener('click', handler=probe, target='background')
chart.add_event_listener('cursortargetchange', handler=probe, target='chart', throttle_ms=80)
chart.add_event_listener('resize', handler=probe, target='chart')

chart.open(live=True)

Chart events

Common events

  • Interaction: click, pointermove, pointerdown, pointerup, pointerenter, pointerleave, dblclick, contextmenu
  • Cursor: cursortargetchange (hover hits / nearest items)
  • Layout: resize, layoutchange
  • View state (chart-specific): e.g., viewchange on map/treemap

Targets (chart)

Choose where to listen:

  • seriesBackground – the plot area inside axes (best for data-space clicks & moves)
  • background – the chart background (outside axes)
  • title – chart title element
  • axisXTitle / axisYTitle – axis title elements
  • chart – the chart object (use for resize, layoutchange, cursortargetchange, etc.)
  • auto – default (for pointer events, typically resolves to the plot area)

Tip: For dense pointer streams, add throttle_ms (e.g., 50–100) to reduce callback frequency.

Examples

ChartXY – cursor targets in the plot area

import lightningchart as lc

chart = lc.ChartXY(theme=lc.Themes.Light, title='Click demo')
s = chart.add_point_series()

def on_move(ev: dict):
print(ev)

chart.add_event_listener('cursortargetchange', handler=on_move, target='chart')
chart.open(live=True)

BarChart – background & title targets

def on_ev(ev: dict):
print(ev)

chart.add_event_listener('cursortargetchange', handler=on_ev, target='chart')
chart.add_event_listener('click', handler=on_ev, target='background')
chart.add_event_listener('click', handler=on_ev, target='title')
chart.add_event_listener('pointermove', handler=on_ev, target='background')
chart.add_event_listener('pointerup', handler=on_ev, target='background')
chart.add_event_listener('pointerleave',handler=on_ev, target='background')
chart.add_event_listener('pointerenter',handler=on_ev, target='background')
chart.add_event_listener('dblclick', handler=on_ev, target='background')
chart.add_event_listener('contextmenu', handler=on_ev, target='background') # right-click event
chart.open(live=True)

MapChart – resize & view state

def on_ready(ev: dict):
print('[ready] map data loaded', ev)

def on_view(ev: dict):
print('[viewchange]', ev)

chart.add_event_listener('ready', handler=on_ready, target='chart' , once=True)
chart.add_event_listener('viewchange', handler=on_view, target='chart')
chart.open(live=True)

Series events

Note: Series listeners do not take a target parameter.

Common events (series)

pointermove, click, pointerdown, pointerup, pointerenter, pointerleave, dblclick, contextmenu

Example

def on_series(e: dict):
print('[series]', e)

series.add_event_listener('pointermove', handler=on_series, throttle_ms=50)
series.add_event_listener('click', handler=on_series)
chart.open(live=True)

Axis events

Note: Axis listeners do not take a target parameter.

Common events (axis)

  • Interaction: click, pointermove, pointerdown, pointerup, pointerenter, pointerleave, dblclick, contextmenu
  • Axis-specific: intervalchange (fires when the axis interval changes due to zooming, panning, or programmatic changes)

Example

import lightningchart as lc

chart = lc.ChartXY(theme=lc.Themes.Light, title='Axis Event Demo')
series = chart.add_line_series()
series.append_samples(x_values=list(range(100)), y_values=list(range(100)))

x_axis = chart.get_default_x_axis()
y_axis = chart.get_default_y_axis()

# Listen for axis interval changes (zoom/pan)
def on_interval_change(event):
start = event.get('start')
end = event.get('end')
print(f"Axis interval: {start} to {end}")

x_axis.add_event_listener('intervalchange', handler=on_interval_change)

# Listen for clicks on the axis
def on_axis_click(event):
print(f"Axis clicked: {event}")

x_axis.add_event_listener('click', handler=on_axis_click)

# Listen for pointer movements (throttled)
def on_axis_move(event):
print(f"Pointer on axis: {event}")

x_axis.add_event_listener('pointermove', handler=on_axis_move, throttle_ms=200)

chart.open(live=True)
tip
  • Start with probe() to inspect the event dicts, then pick the fields you need.
  • Use throttle_ms for high-frequency events to reduce CPU/IO.
  • once=True is handy for one-shot interactions (e.g., first click to start/stop).
  • Disable pointer events on overlays: Call .set_pointer_events(False) on series (rectangles, text, etc.) that should not block clicks to the plot area under them.
import lightningchart as lc
import numpy as np
import pandas as pd

lc.set_license('my-license-key')

np.random.seed(20)
dash = lc.Dashboard(columns=15, rows=1, theme=lc.Themes.Dark)
controls = dash.ChartXY(0, 0, 2, 1, legend={'visible': False})
chart = dash.ChartXY(2, 0, 13, 1, legend={'position': 'BottomCenter'})

# Setup control panel
controls.get_default_x_axis().set_tick_strategy('Empty').set_interval(0, 100, stop_axis_after=True)
controls.get_default_y_axis().set_tick_strategy('Empty').set_interval(0, 100, stop_axis_after=True)
controls.set_title('')

# Generate data
dates = pd.date_range(pd.Timestamp.now() - pd.Timedelta(days=1825), pd.Timestamp.now(), freq='D')
x_ms = (dates.astype('int64') // 10**6).tolist()
y_vals = (np.cumsum(np.random.normal(0, 1, len(dates))) + 50).tolist()

series = chart.add_line_series()
series.set_palette_line_coloring(
steps=[
{'value': 0, 'color': '#0040ff'},
{'value': 25, 'color': '#00ff40'},
{'value': 50, 'color': '#ff4000'},
],
look_up_property='y',
)
series.append_samples(x_values=x_ms, y_values=y_vals)
chart.get_default_x_axis().set_tick_strategy('DateTime')

buttons = [
('7d', lambda: (x_ms[-7], x_ms[-1])),
('30d', lambda: (x_ms[-30], x_ms[-1])),
('90d', lambda: (x_ms[-90], x_ms[-1])),
('6m', lambda: (x_ms[-180], x_ms[-1])),
('1y', lambda: (x_ms[-365], x_ms[-1])),
('2y', lambda: (x_ms[-720], x_ms[-1])),
('Max', lambda: (x_ms[0], x_ms[-1]))
]

btn_rects = controls.add_rectangle_series().set_pointer_events(False)
btn_labels = controls.add_text_series().set_pointer_events(False)
btn_bounds = []

y = 88
for label, _ in buttons:
rect = btn_rects.add(10, y, 90, y + 10)
rect.set_stroke(1, (200, 200, 200)).set_color((40, 120, 220, 110))
btn_labels.add(50, y + 5, label).set_color('#ffffff').set_font(12)
btn_bounds.append((10, y, 90, y + 10, rect))
y -= 14

# Click handler
def on_click(evt):
x, y = evt.get('axis', {}).get('x'), evt.get('axis', {}).get('y')
if x is None or y is None:
return

for x1, y1, x2, y2, rect in btn_bounds:
if x1 <= x <= x2 and y1 <= y <= y2:
idx = btn_bounds.index((x1, y1, x2, y2, rect))
lo, hi = buttons[idx][1]()
chart.get_default_x_axis().set_interval(lo, hi, stop_axis_after=True)

# Highlight selected
for _, _, _, _, r in btn_bounds:
r.set_color((40, 120, 220, 110) if r != rect else (255, 180, 60, 150))
break

controls.add_event_listener('pointerdown', handler=on_click, target='seriesBackground')
dash.open(live=True)