Skip to main content
v2.2

Cursor

CursorCursor

LightningChart Python provides powerful built-in cursor functionality (CursorXY / Cursor2D / Cursor3D) that activates when users place their mouse over a chart. The cursor displays contextual information about data points, series, and chart components, enhancing data exploration and analysis.

Cursor Control Methods

Cursors can be controlled in two primary ways:

  1. Built-in cursor modes — Automatic hover/nearest/pointed logic controlled by the chart
  2. Manual cursors — Created with chart.add_cursor() for full programmatic control

Built-in Cursor Modes

Cursor modes define how the cursor targets and displays data. Each mode offers different interaction behaviors:

Available Cursor Modes

ModeDescription
"show-nearest"Show the nearest value to reference location
"show-nearest-interpolated"Show the nearest interpolated value to reference location
"show-pointed"Shows the exact pointed value (directly under user pointer)
"show-pointed-interpolated"Shows the pointed interpolated value (directly under user pointer)
"show-all"Displays values from all trackables simultaneously.
"show-all-interpolated"Shows interpolated values from all trackables simultaneously.
"disabled"Completely disables the cursor

Setting Cursor Modes

# Enable cursor with different targeting modes
chart.set_cursor_mode("show-nearest") # show one series (nearest)
chart.set_cursor_mode("show-all") # show all series at once
chart.set_cursor_mode("show-nearest-interpolated") # with interpolation
chart.set_cursor_mode("disabled") # disable cursor

# Access the cursor instance for further customization
cursor = chart.set_cursor() # Returns Cursor instance
cursor.set_visible(True)
cursor.set_auto_fit(True)

# Check current cursor mode
current_mode = chart.get_cursor_mode()
print(f"Current cursor mode: {current_mode}")

Per-Series Cursor Control

You can enable or disable cursor interaction for individual series:

# Create series
series1 = chart.add_line_series()
series2 = chart.add_line_series()

# Disable cursor for specific series
series1.set_cursor_enabled(False) # Cursor will ignore this series

# Check cursor state for a series
is_enabled = series2.get_cursor_enabled()
Performance & Best Practices
  • Cursor visibility can be affected by hover/activation behavior in auto-cursor modes.
  • For large datasets (millions of points), use "show-pointed" instead of nearest-search modes for better performance.
  • Interpolated modes provide smoother cursor feedback but may not reflect actual data values.
  • Use chart.set_cursor_enabled_during_axis_animation(False) to disable cursor during zoom/pan for improved responsiveness.

Point Marker Customization

The point marker is the visual indicator that appears on data points when the cursor is active. Customize its appearance to match your chart's branding or UX requirements.

Basic Point Marker Configuration

cursor = chart.set_cursor()

cursor.set_point_marker(
shape="triangle", # Shape: 'circle', 'square', 'triangle', 'diamond', 'cross', 'plus', 'star'
size=18, # Size in pixels (or tuple for width/height)
fill_color="#25D219", # Fill color
stroke_thickness=2, # Border thickness
stroke_color="#F10B0B", # Border color
rotation=15, # Rotation in degrees
visible=True, # Show/hide marker
origin={'x': 1, 'y': 1}, # Anchor point [-1, 1], 0 is center
pointer_events=True, # Enable mouse interactions
)

# Get current visibility state
is_visible = cursor.get_point_marker_visible()

# Hide/show point marker
cursor.set_point_marker_visible(False)

Available Point Marker Shapes

ShapeDescription
'circle'Round marker (default)
'square'Square marker
'triangle'Triangular marker
'diamond'Diamond-shaped marker
'cross'Cross/X-shaped marker
'plus'Plus/+-shaped marker
'star'Star-shaped marker

Advanced Size and Positioning

cursor.set_point_marker(
size={'x': 20, 'y': 30}, # Different width and height
origin={'x': -1, 'y': 0}, # Position relative to data point
margin={'left': 5, 'right': 5, 'top': 3, 'bottom': 3} # Margin around marker
)

Dynamic Cursor Behavior

Automatically match cursor styling to hovered series properties:

# Match cursor style to series data
chart.set_cursor_dynamic_behavior(
match_point_marker_shape=True, # Match series marker shape
point_marker_fill='match-data', # Use series color
point_marker_stroke={'color': 'match-data', 'thickness': 2},
point_marker_size=(15, 15)
)

# Disable dynamic behavior
chart.set_cursor_dynamic_behavior(None) # disabled

Listening to Point Marker Events

Add interactive behavior by listening to pointer events on the cursor marker:

def on_cursor_click(event):
client_pos = event.get('client')
print(f"Cursor marker clicked at: {client_pos}")

cursor.set_point_marker(
shape="circle",
size=15,
pointer_events=True, # Required for events
event_listeners=[
{
"event": "click",
"handler": on_cursor_click,
"throttle_ms": 100, # Optional: minimum delay between events
"once": False # Optional: auto-remove after first trigger
}
]
)

Supported Point Marker Events

EventDescription
'click'Single click on marker
'dblclick'Double click on marker
'pointerdown'Mouse button pressed
'pointerup'Mouse button released
'pointerenter'Pointer enters marker area
'pointerleave'Pointer leaves marker area
'pointermove'Pointer moves over marker
'wheel'Mouse wheel scrolled over marker

Complete Event Listener Example

cursor.set_point_marker(
shape="star",
size=20,
fill_color="#FFD700",
pointer_events=True,
event_listeners=[
{
"event": name,
"handler": lambda ev, n=name: print(f"[cursor] {n} at {ev.get('client')}")
}
for name in ["click", "pointerdown", "pointerup",
"pointerenter", "pointerleave", "pointermove", "wheel"]
]
)
Event Handler Details
  • Each handler receives an event dict containing DOM coordinates via event.get('client')
  • Use "throttle_ms": 100 to limit event frequency (useful for pointermove)
  • Set "once": True to automatically remove the listener after first trigger
  • pointer_events must be True for events to work (automatically enabled when event_listeners are provided)

Result Table Configuration

The result table is the tooltip that displays data values and information when the cursor is active. Fully customize its appearance, positioning, and content.

Basic Result Table Styling

cursor = chart.set_cursor()

cursor.set_result_table(
origin={'x': -1, 'y': -1}, # Position relative to cursor
padding=10, # Internal padding
margin=6, # External margin
text_color="#CB1818", # Text color
font_size=11, # Font size in pixels
font_family="Arial, sans-serif", # Font family
font_weight="bold", # Font weight
font_style="normal", # Font style
background_fill_color="#3385BF", # Background color
background_stroke_thickness=1, # Border thickness
background_stroke_color="#C71BA7", # Border color
background_corner_radius=8, # Rounded corners
visible=True, # Show/hide table
effect=False, # Disable theme effects
)

# Check result table visibility
is_visible = cursor.get_result_table_visible()

# Toggle visibility
cursor.set_result_table_visible(True)

Result Table Positioning

The origin parameter controls where the result table appears relative to the cursor:

# Position table to top-left of cursor
cursor.set_result_table(origin={'x': -1, 'y': -1})

# Position table to bottom-right
cursor.set_result_table(origin={'x': 1, 'y': 1})

# Center the table on cursor
cursor.set_result_table(origin={'x': 0, 'y': 0})

# Position with tuples
cursor.set_result_table(origin=(0.5, -1)) # Centered horizontally, above cursor
Origin XPosition (Horizontal)
-1Left of cursor
0Centered
1Right of cursor
Origin YPosition (Vertical)
-1Above cursor
0Centered
1Below cursor

Auto-fit Behavior

Enable auto-fit to keep the result table within visible chart boundaries:

# Enable auto-fit (keeps table in view)
cursor.set_auto_fit(True)

# Check auto-fit state
is_autofit = cursor.get_auto_fit()

Result Table Padding and Margin

cursor.set_result_table(
# Uniform padding on all sides
padding=10,

# Or specify individual sides
padding={'left': 15, 'right': 15, 'top': 10, 'bottom': 10},

# Margin (space between table and cursor point)
margin={'left': 8, 'right': 8, 'top': 5, 'bottom': 5}
)

CursorXY Grid Lines and Tick Markers

For XY charts, customize the cursor's crosshair grid lines and axis tick markers to integrate seamlessly with your chart design.

Grid Line Styling

Control the appearance of the vertical and horizontal cursor lines:

cursor = chart.set_cursor()

# Style X grid line (vertical)
cursor.set_grid_stroke_x_style(thickness=2, color="red")

# Style Y grid line (horizontal)
cursor.set_grid_stroke_y_style(thickness=1, color="#00aa00")

# Get current grid styles
x_style = cursor.get_grid_stroke_x_style() # Returns {'thickness': 2, 'color': '...'}
y_style = cursor.get_grid_stroke_y_style() # Returns {'thickness': 1, 'color': '...'}

Grid Line Cutting

Control whether grid lines are "cut" (interrupted) at the data point:

# Enable/disable grid line cutting
cursor.set_grid_stroke_x_cut(False) # Continuous line
cursor.set_grid_stroke_y_cut(True) # Line cut at data point

# Check cut state
x_cut = cursor.get_grid_stroke_x_cut() # Returns bool
y_cut = cursor.get_grid_stroke_y_cut() # Returns bool

Cursor Boundary and Space Allocation

Control cursor behavior relative to axis boundaries:

# Keep cursor within axis boundaries
cursor.set_keep_within_axis_boundaries(True)
is_constrained = cursor.get_keep_within_axis_boundaries()

# Control whether tick markers allocate space on axes
cursor.set_tick_markers_allocate_axis_space(False)
allocates_space = cursor.get_tick_markers_allocate_axis_space()

Axis Tick Marker Styling

Independently style the X and Y axis tick markers that display cursor values:

# Style X-axis tick marker
cursor.set_tick_marker_x(
text_color="#FFFFFF",
text_rotation=-45, # Rotate text
font_family="Courier New",
font_size=10,
font_weight="bold",
font_style="italic",
margin=4, # Margin around tick
background_fill_color="#CB1818", # Background color
background_stroke_thickness=1, # Border thickness
background_stroke_color="#C71BA7", # Border color
visible=True,
)

# Style Y-axis tick marker
cursor.set_tick_marker_y(
text_color="#00FF00",
font_size=12,
font_weight="normal",
background_fill_color="#333333",
background_stroke_thickness=2,
background_stroke_color="#FFFFFF",
visible=True,
)

# Control tick marker visibility individually
cursor.set_tick_marker_x_visible(True)
cursor.set_tick_marker_y_visible(False)

# Check visibility
x_visible = cursor.get_tick_marker_x_visible()
y_visible = cursor.get_tick_marker_y_visible()

Complete CursorXY Configuration Example

chart = lc.ChartXY()
chart.set_cursor_mode("show-nearest")
cursor = chart.set_cursor()

# Configure grid lines
cursor.set_grid_stroke_x_style(thickness=1, color="#FF6B6B")
cursor.set_grid_stroke_y_style(thickness=1, color="#4ECDC4")
cursor.set_grid_stroke_x_cut(True)
cursor.set_grid_stroke_y_cut(True)

# Configure tick markers
cursor.set_tick_marker_x(
text_color="#FFFFFF",
font_size=11,
font_weight="bold",
background_fill_color="#FF6B6B",
background_stroke_thickness=1,
background_stroke_color="#000000",
margin={'left': 6, 'right': 6, 'top': 3, 'bottom': 3}
)

cursor.set_tick_marker_y(
text_color="#000000",
font_size=11,
font_weight="bold",
background_fill_color="#4ECDC4",
background_stroke_thickness=1,
background_stroke_color="#000000",
margin=5
)

# Boundary settings
cursor.set_keep_within_axis_boundaries(True)
cursor.set_tick_markers_allocate_axis_space(False)

Cursor3D Tick Styling

3D cursors support the same cursor modes as XY charts, including "show-nearest", "show-pointed", and other targeting modes. Configure tick marker styling for the X, Y, and Z axes.

Styling All Axes at Once

Use set_ticks() to apply styling to all three axes (X, Y, Z) simultaneously:

chart = lc.Chart3D()
chart.set_cursor_mode("show-pointed")
cursor = chart.set_cursor()

cursor.set_ticks(
text_color="#df620e",
text_rotation=30,
font_family="Courier New",
font_size=18,
font_weight="bold",
font_style="italic",
text_padding=4, # Padding around text
tick_length=20, # Length of tick line
tick_stroke_color="#e0e01a", # Tick line color
tick_stroke_thickness=4, # Tick line thickness
background_fill_color="#123d95", # Background color
background_stroke_color="#e01aa5", # Border color
background_stroke_thickness=2, # Border thickness
padding={"left": 24, "right": 6, "top": 4, "bottom": 4},
)

Styling Individual Axes

Override specific properties for individual axes:

# Style X-axis tick
cursor.set_tick_x(
font_size=16,
tick_length=14,
text_color="#FF0000",
background_fill_color="#330000"
)

# Style Y-axis tick
cursor.set_tick_y(
text_color="#00FFAA",
font_size=14,
tick_stroke_color="#00FF00",
tick_stroke_thickness=2
)

# Style Z-axis tick
cursor.set_tick_z(
background_fill_color="#222222",
background_stroke_color="#FFFFFF",
background_stroke_thickness=1
)

Tick Parameters Reference

ParameterTypeDescription
text_colorColorColor of tick text
text_rotationfloatRotation angle in degrees
font_sizefloatFont size in pixels
font_familystrFont family name
font_weightstrFont weight ('normal', 'bold', etc.)
font_stylestrFont style ('normal', 'italic')
text_paddingfloatPadding around text
tick_lengthfloatLength of tick line in pixels
tick_stroke_thicknessfloatThickness of tick line
tick_stroke_colorColorColor of tick line
background_fill_colorColorBackground fill color
background_stroke_thicknessfloatBackground border thickness
background_stroke_colorColorBackground border color
paddingint/float/dictPadding around entire tick component

Complete 3D Cursor Example

chart = lc.Chart3D()
chart.set_cursor_mode("show-pointed")
cursor = chart.set_cursor()

# Apply base styling to all axes
cursor.set_ticks(
font_family="Consolas",
font_size=12,
font_weight="bold",
text_padding=5,
tick_length=15,
tick_stroke_thickness=2,
background_stroke_thickness=1,
padding=5
)

# Customize individual axes with colors
cursor.set_tick_x(
text_color="#FF5555",
tick_stroke_color="#FF5555",
background_fill_color="#330000"
)

cursor.set_tick_y(
text_color="#55FF55",
tick_stroke_color="#55FF55",
background_fill_color="#003300"
)

cursor.set_tick_z(
text_color="#5555FF",
tick_stroke_color="#5555FF",
background_fill_color="#000033"
)

# Configure point marker and result table
cursor.set_point_marker(shape="sphere", size=8, fill_color="#FFFFFF")
cursor.set_result_table(
visible=True,
background_fill_color="#1a1a1a",
text_color="#FFFFFF"
)

Combining with Axis Grid Lines

Extend tick lines across chart planes using axis grid controls:

# Extend grid lines across 3D space
x_axis = chart.get_default_x_axis()
y_axis = chart.get_default_y_axis()
z_axis = chart.get_default_z_axis()

x_axis.set_grid_stroke_length(100)
x_axis.set_grid_stroke_style(thickness=1, color="#555555")

y_axis.set_grid_stroke_length(100)
y_axis.set_grid_stroke_style(thickness=1, color="#555555")

z_axis.set_grid_stroke_length(100)
z_axis.set_grid_stroke_style(thickness=1, color="#555555")

Custom Cursor Formatting

Customize the content and appearance of the cursor result table using formatter callbacks. The formatter receives cursor event data and returns a table structure.

Basic Cursor Formatting

chart.set_cursor_mode("show-nearest")
cursor = chart.set_cursor()

def cursor_formatter(event: dict):
"""Format cursor result table content."""
hits = event.get("hits", [])

if not hits:
return []

hit = hits[0]
x_val = float(hit.get("x", 0.0))
y_val = float(hit.get("y", 0.0))
name = hit.get("seriesName", "Series")

return [
[
'<span style="opacity:.7;text-transform:uppercase;letter-spacing:.08em;">Time</span>',
f'<span style="font-weight:700;">{x_val:.2f} ms</span>',
],
[
'<span style="opacity:.7;text-transform:uppercase;letter-spacing:.08em;">Value</span>',
f'<span style="color:#34D399;font-weight:700;">{y_val:.2f} °C</span>',
],
[
'<span style="opacity:.7;">Series</span>',
f'<span style="letter-spacing:.12em;">{name}</span>',
],
]

# Apply formatter to chart
chart.set_cursor_formatting(cursor_formatter)

# Get current formatter
current_formatter = chart.get_cursor_formatting()

# Remove formatter (revert to default)
chart.set_cursor_formatting(None)

chart.open(live=True)

Per-Series Custom Formatting

Override cursor formatting for individual series:

series1 = chart.add_line_series()
series2 = chart.add_line_series()

def series_specific_formatter(event: dict):
"""Custom formatter for a specific series."""
hit = event.get('hit', {})
i_sample = hit.get('iSample')
if i_sample is not None:
return [
["Series", "", hit.get("seriesName", "")],
["X", "", f"{hit.get('x', 0.0):.2f}"],
["Y", "", f"{hit.get('y', 0.0):.2f}"]
]

# Set formatter for specific series
series1.set_cursor_formatting_override(series_specific_formatter)

# Check if series has custom formatter
has_override = series1.get_cursor_formatting_override()

# Remove series-specific formatter
series1.set_cursor_formatting_override(None)

Advanced Formatting with HTML Styling

Use inline CSS to style cursor content:

def advanced_formatter(event: dict):
hits = event.get("hits", [])
if not hits:
return []

hit = hits[0]
x = hit.get("x", 0)
y = hit.get("y", 0)
series_name = hit.get("seriesName", "Unknown")

# Determine status based on value
status_color = "#22C55E" if y > 50 else "#EF4444"
status_text = "HIGH" if y > 50 else "LOW"

return [
# Header row with bold styling
[
'<span style="font-weight:800;font-size:1.1em;color:#A0AEC0;">DATA POINT</span>',
""
],
# X value with units
[
'<span style="opacity:0.7;text-transform:uppercase;letter-spacing:0.08em;">Time</span>',
f'<span style="font-weight:700;font-family:monospace;">{x:.2f}</span> <span style="opacity:0.6;font-size:0.9em;">ms</span>'
],
# Y value with conditional styling
[
'<span style="opacity:0.7;text-transform:uppercase;letter-spacing:0.08em;">Value</span>',
f'<span style="font-weight:700;color:{status_color};">{y:.2f}</span> <span style="opacity:0.6;font-size:0.9em;">°C</span>'
],
# Status indicator
[
'<span style="opacity:0.7;">Status</span>',
f'<span style="padding:2px 8px;background-color:{status_color};color:white;border-radius:3px;font-weight:600;font-size:0.85em;">{status_text}</span>'
],
# Series name
[
'<span style="opacity:0.7;">Series</span>',
f'<span style="letter-spacing:0.12em;font-style:italic;">{series_name}</span>'
]
]

chart.set_cursor_formatting(advanced_formatter)

Custom Cursor Implementation

For complete control, replace the built-in cursor with a custom implementation:

def custom_cursor_handler(event: dict):
hits = event.get("hits", [])

if hits:
for hit in hits:
print(f"Hit: {hit.get('seriesName')} at ({hit.get('x')}, {hit.get('y')})")

# Your custom cursor rendering logic here
# This completely replaces the built-in cursor

# Set custom cursor handler
chart.set_custom_cursor(
handler=custom_cursor_handler,
throttle_ms=50 # Limit update frequency
)

# Remove custom cursor (restore built-in)
chart.set_custom_cursor(None)

Formatting Tips

Styling Best Practices
  • Content vs Container: Formatters control text content and inline styles. Use cursor.set_result_table(...) for container styling (background, padding, borders).
  • HTML Support: Result table cells support inline HTML and CSS for rich formatting.
  • Performance: Use throttle_ms for custom cursors that update frequently to maintain smooth performance.
  • Return Format: Formatters should return a list of rows, where each row is a list of cells (strings).
  • Error Handling: Always check if hits array exists and has content before accessing data.

Manual Cursors

Manual cursors provide full programmatic control over cursor position and content. Unlike built-in cursor modes, manual cursors don't respond to hover or nearest-point logic — you control everything.

Creating Manual Cursors

# Create a manual cursor
cursor = chart.add_cursor()

# Position the cursor (XY coordinates)
cursor.set_position(x=60, y=60)

# For 3D charts, provide x, y, z coordinates
cursor_3d = chart3d.add_cursor()
cursor_3d.set_position(x=50, y=75, z=100)

Setting Manual Cursor Content

Important: Manual cursors require explicit result table content. The table won't auto-populate from data.

cursor = chart.add_cursor()
cursor.set_position(x=60, y=60)

# REQUIRED: Provide content for the result table
cursor.set_result_table(
visible=True,
content=[
["X", "60"],
["Y", "60"],
["Series", "Manual Data"]
],
text_color="#FFFFFF",
background_fill_color="#FF5500",
padding=10,
margin=5
)

# Style the point marker
cursor.set_point_marker(
shape="triangle",
size=15,
fill_color="#FF5500",
stroke_thickness=2,
stroke_color="#FFFFFF",
visible=True
)

Multiple Manual Cursors

Create multiple independent cursors for comparison or annotations:

# First cursor - Red
cursor1 = chart.add_cursor()
cursor1.set_position(x=25, y=50)
cursor1.set_point_marker(shape="circle", size=10, fill_color="#FF0000", visible=True)
cursor1.set_result_table(
visible=True,
content=[["Point A", "25"], ["Value", "50"]],
background_fill_color="#FF0000"
)

# Second cursor - Blue
cursor2 = chart.add_cursor()
cursor2.set_position(x=75, y=80)
cursor2.set_point_marker(shape="square", size=10, fill_color="#0000FF", visible=True)
cursor2.set_result_table(
visible=True,
content=[["Point B", "75"], ["Value", "80"]],
background_fill_color="#0000FF"
)

# Third cursor - Green (hidden result table)
cursor3 = chart.add_cursor()
cursor3.set_position(x=50, y=65)
cursor3.set_point_marker(shape="diamond", size=12, fill_color="#00FF00", visible=True)
cursor3.set_result_table_visible(False) # Only show marker, no table

Dynamic Manual Cursor Updates

Update manual cursor position and content dynamically:

import lightningchart as lc
import time

chart = lc.ChartXY()
chart.open(live=True)

# Create manual cursor
cursor = chart.add_cursor()
cursor.set_point_marker(shape="circle", size=12, fill_color="#FFD700", visible=True)

# Animate cursor across chart
for x in range(0, 100, 5):
y = x ** 0.5 * 10 # Square root curve

# Update position
cursor.set_position(x=x, y=y)

# Update result table content
cursor.set_result_table(
visible=True,
content=[
["X", f"{x:.1f}"],
["Y", f"{y:.1f}"],
["Progress", f"{x}%"]
],
background_fill_color="#333333",
text_color="#FFD700"
)

time.sleep(0.1)

Disposing Manual Cursors

Remove manual cursors when no longer needed:

cursor1 = chart.add_cursor()
cursor2 = chart.add_cursor()

# ... use cursors ...

# Remove specific cursor
cursor1.dispose()

# Remove all by disposing the chart
# (built-in cursors and manual cursors will be cleaned up)
chart.dispose()

Manual Cursor Use Cases

Manual cursors are ideal for:

  1. Static Annotations: Mark specific points of interest

    marker = chart.add_cursor()
    marker.set_position(x=peak_x, y=peak_y)
    marker.set_result_table(
    visible=True,
    content=[["Peak", f"{peak_y:.2f}"]]
    )
  2. Data Comparison: Show multiple data points simultaneously

    cursor_min = chart.add_cursor()
    cursor_min.set_position(x=min_x, y=min_val)
    cursor_min.set_point_marker(fill_color="#FF0000")

    cursor_max = chart.add_cursor()
    cursor_max.set_position(x=max_x, y=max_val)
    cursor_max.set_point_marker(fill_color="#00FF00")
  3. Animation & Simulation: Programmatically move cursors to visualize processes

    simulation_cursor = chart.add_cursor()
    for time_step in simulation_data:
    simulation_cursor.set_position(x=time_step.x, y=time_step.y)
    time.sleep(0.05)
  4. Custom User Interaction: Respond to events and position cursors accordingly

    def on_chart_click(event):
    # Get axis coordinates from event
    axis = event.get('axis', {})
    x = axis.get('x')
    y = axis.get('y')

    if x is not None and y is not None:
    manual_cursor.set_position(x=x, y=y)
    manual_cursor.set_result_table(
    visible=True,
    content=[["Clicked at", f"({x:.1f}, {y:.1f})"]]
    )

    chart.add_event_listener('click', handler=on_chart_click, target='seriesBackground')
Event Coordinate Access

When handling chart events, coordinates are available in nested dictionaries:

  • Axis coordinates: event.get('axis', {}).get('x') and event.get('axis', {}).get('y')
  • Screen pixels: event.get('client', {}).get('clientX') and event.get('client', {}).get('clientY')
  • Relative position: event.get('relative', {}).get('x') and event.get('relative', {}).get('y')

Always use target='seriesBackground' to get axis coordinates when clicking on the chart data area.

Manual Cursor Notes
  • Position Control: Manual cursors require explicit set_position() calls. They don't respond to set_cursor_mode().
  • Content Required: Result table content must be set via set_result_table(content=...) or it will remain empty.
  • Visibility: Use set_visible(), set_point_marker_visible(), and set_result_table_visible() independently.
  • Persistence: Manual cursors remain at their set position until moved or disposed.

Cursor Visibility and State Management

Control cursor visibility and retrieve current state information:

Visibility Control

cursor = chart.set_cursor()

# Control overall cursor visibility
cursor.set_visible(True)
is_visible = cursor.get_visible()

# Control point marker visibility
cursor.set_point_marker_visible(True)
marker_visible = cursor.get_point_marker_visible()

# Control result table visibility
cursor.set_result_table_visible(True)
table_visible = cursor.get_result_table_visible()

# Control cursor during axis animations (zoom/pan)
chart.set_cursor_enabled_during_axis_animation(False)
enabled_during_animation = chart.get_cursor_enabled_during_axis_animation()
Understanding set_cursor_mode() vs cursor.set_visible()

These methods control different layers of cursor behavior:

chart.set_cursor_mode(...) - Controls the auto-cursor system

  • Enables/disables hover logic and nearest-point solving
  • "disabled" = hard off - no cursor behavior at all
  • Other modes ("show-nearest", etc.) = auto-cursor system is active

cursor.set_visible(...) - Controls the visibility flag only

  • When auto-cursor mode is enabled, the library may override this based on hover/activation
  • Useful for:
    • Starting hidden, then showing later (UI toggle)
    • Manual cursor visibility control
    • Hiding specific components while keeping mode logic active

Example - Start hidden, show on demand:

chart.set_cursor_mode("show-nearest")  # Auto-cursor enabled
cursor = chart.set_cursor()
cursor.set_visible(False) # Start hidden

# Later (e.g., button click)
cursor.set_visible(True) # Show cursor

To completely disable cursor (no hover, no rendering):

chart.set_cursor_mode("disabled")      # Hard off

To keep cursor logic but hide UI elements:

chart.set_cursor_mode("show-nearest")  # Logic still runs
cursor = chart.set_cursor()
cursor.set_point_marker_visible(False) # Hide marker
cursor.set_result_table_visible(False) # Hide tooltip
# Cursor still tracks data, just doesn't render

Cursor State Getters

Retrieve current cursor configuration:

# Get cursor mode
mode = chart.get_cursor_mode() # Returns: 'show-nearest', 'disabled', etc.

# Get auto-fit state
auto_fit = cursor.get_auto_fit()

# Get visibility states
cursor_visible = cursor.get_visible()
marker_visible = cursor.get_point_marker_visible()
table_visible = cursor.get_result_table_visible()

# For CursorXY - get grid line styles
x_grid_style = cursor.get_grid_stroke_x_style()
y_grid_style = cursor.get_grid_stroke_y_style()

# Get grid cut states
x_cut = cursor.get_grid_stroke_x_cut()
y_cut = cursor.get_grid_stroke_y_cut()

# Get tick marker visibility
x_tick_visible = cursor.get_tick_marker_x_visible()
y_tick_visible = cursor.get_tick_marker_y_visible()

# Get boundary and space settings
within_boundaries = cursor.get_keep_within_axis_boundaries()
allocates_space = cursor.get_tick_markers_allocate_axis_space()