Crosshair Explorer
CompositeChart crosshair tracking with live coordinate readout, right-click alert placement, and a synced three-chart dashboard with HoverCard-managed alerts.
Overview
CompositeChart crosshair tracking with live coordinate readout, right-click alert placement, and a synced three-chart dashboard with HoverCard-managed alerts.
The examples on this page read a MUI X Pro license key from the MUI_PRO_API_KEY environment variable and pass it as licenseKey. Without it the charts render with the unlicensed watermark.
Live examples
# File: docs/crosshair/demo.py
"""Crosshair Explorer demo — rendered on /crosshair via `.. exec::`.
Ported verbatim from the pre-migration pages/crosshair.py (same ids, same callbacks).
"""
import os
import math
import random
from datetime import datetime, timedelta
import dash_mantine_components as dmc
from dash import html, dcc, callback, Input, Output, State, ctx, no_update, ALL
from dash_iconify import DashIconify
from dash_mui_charts import CompositeChart
MUI_LICENSE_KEY = os.environ.get('MUI_PRO_API_KEY', '')
# ---------------------------------------------------------------------------
# Data
# ---------------------------------------------------------------------------
random.seed(99)
N = 2016
BASE = datetime(2025, 6, 1)
TIMESTAMPS = [int((BASE + timedelta(minutes=5 * i)).timestamp()) * 1000 for i in range(N)]
def _random_walk(start, mean, noise, daily_amp, n):
val, out = start, []
for i in range(n):
daily = math.sin(2 * math.pi * i / 288)
val += 0.02 * (mean - val) + random.gauss(0, noise) + daily_amp * daily
out.append(round(val, 2))
return out
TEMP_DATA = _random_walk(22, 22, 0.15, 0.8, N)
PRESSURE_DATA = _random_walk(1013, 1013, 0.1, 0.3, N)
HUMIDITY_DATA = _random_walk(55, 55, 0.2, 0.6, N)
CHART_META = {
'temp': {'label': 'Temperature', 'unit': 'C', 'color': '#1976d2', 'data': TEMP_DATA},
'pressure': {'label': 'Pressure', 'unit': 'hPa', 'color': '#7b1fa2', 'data': PRESSURE_DATA},
'humidity': {'label': 'Humidity', 'unit': '%', 'color': '#00838f', 'data': HUMIDITY_DATA},
}
ALERT_COLORS = ['#e53935', '#ff9800', '#4caf50', '#2196f3', '#9c27b0', '#00bcd4']
# Chart layout constants for pixel mapping
CHART_HEIGHTS = {'temp': 200, 'pressure': 200, 'humidity': 230}
MARGIN = {'top': 10, 'bottom': 5, 'right': 15}
MARGIN_BOT_VISIBLE = 55 # bottom chart has x-axis
# Styles
section_style = {'marginBottom': '40px'}
description_style = {'color': 'var(--mantine-color-dimmed)', 'marginBottom': '12px', 'fontSize': '14px'}
code_style = {
'backgroundColor': 'var(--mantine-color-default)', 'padding': '12px', 'borderRadius': '5px',
'whiteSpace': 'pre-wrap', 'fontSize': '11px', 'overflow': 'auto',
}
coord_bar = {
'fontFamily': 'monospace', 'fontSize': '14px', 'padding': '10px 16px',
'backgroundColor': '#1a1a1a', 'color': '#a0e8d8', 'borderRadius': '10px',
'marginBottom': '10px', 'minHeight': '40px', 'display': 'flex',
'alignItems': 'center', 'gap': '24px', 'flexWrap': 'wrap',
'backdropFilter': 'blur(10px)',
}
X_AXIS_BASE = {
'id': 'x', 'data': TIMESTAMPS, 'scaleType': 'time',
'tickNumber': 7, 'tickMinStep': 86400 * 1000,
'dateFormat': 'dd/MM/YYYY HH:mm', 'dateTickFormat': 'dd/MM HH:mm',
'tickLabelStyle': {'fontSize': 11}, 'labelStyle': {'fontSize': 12},
}
X_HIDDEN = [{**X_AXIS_BASE, 'position': 'none'}]
X_VISIBLE = [{**X_AXIS_BASE, 'label': 'Date', 'height': 40}]
def _fmt_ts(ms):
try:
return datetime.fromtimestamp(ms / 1000).strftime('%d/%m/%Y %H:%M')
except Exception:
return str(ms)
def _y_to_pixel(y_val, data, chart_height, bottom_margin):
"""Map a y data value to a pixel offset from the top of the chart div."""
vals = [v for v in data if v is not None]
if not vals:
return 0
y_min, y_max = min(vals), max(vals)
# Add 5% padding to match domainLimit='nice'
pad = (y_max - y_min) * 0.08
y_min -= pad
y_max += pad
if y_max == y_min:
return MARGIN['top']
draw_h = chart_height - MARGIN['top'] - bottom_margin
frac = (y_val - y_min) / (y_max - y_min)
# SVG y increases downward, data increases upward
return MARGIN['top'] + draw_h * (1 - frac)
# ---------------------------------------------------------------------------
# Alert tag component — anchored to chart right edge
# ---------------------------------------------------------------------------
def _build_inline_tag(alert, idx, prefix, chart_key, chart_height, bottom_margin):
"""Build a HoverCard tag positioned at the alert's y-pixel on the chart."""
color = ALERT_COLORS[idx % len(ALERT_COLORS)]
meta = CHART_META.get(chart_key, {})
unit = meta.get('unit', '')
data = meta.get('data', [])
condition = alert.get('condition', 'none')
cond_sym = {'above': '\u25B2', 'below': '\u25BC'}.get(condition, '')
pixel_y = _y_to_pixel(alert['y'], data, chart_height, bottom_margin)
tag_label = f"{alert['y']:.1f} {cond_sym}"
return html.Div(
dmc.HoverCard(
withArrow=True, width=200, shadow='lg', position='left',
openDelay=100, closeDelay=200,
children=[
dmc.HoverCardTarget(
html.Div(tag_label, style={
'fontSize': '10px', 'fontWeight': 600, 'color': color,
'backgroundColor': 'var(--mantine-color-body)', 'border': f'1.5px solid {color}',
'borderRadius': '6px', 'padding': '1px 7px',
'cursor': 'pointer', 'lineHeight': '16px',
'boxShadow': '0 1px 4px rgba(0,0,0,0.08)',
'whiteSpace': 'nowrap', 'userSelect': 'none',
}),
),
dmc.HoverCardDropdown(
html.Div([
# Header
html.Div([
html.Div(f"{meta.get('label', '')} Alert", style={
'fontWeight': 600, 'fontSize': '13px', 'color': 'var(--mantine-color-text)',
}),
html.Div(f"{alert['y']:.2f} {unit}", style={
'fontSize': '20px', 'fontWeight': 700, 'color': color,
'letterSpacing': '-0.5px', 'marginTop': '2px',
}),
html.Div(alert.get('x_label', ''), style={
'fontSize': '11px', 'color': 'var(--mantine-color-dimmed)', 'marginTop': '2px',
}),
], style={'marginBottom': '12px'}),
# Condition selector
html.Div("Trigger", style={
'fontSize': '10px', 'fontWeight': 600, 'color': 'var(--mantine-color-dimmed)',
'textTransform': 'uppercase', 'letterSpacing': '0.5px',
'marginBottom': '6px',
}),
dmc.SegmentedControl(
id={'type': f'{prefix}-cond', 'index': idx},
data=[
{'value': 'above', 'label': 'Above'},
{'value': 'none', 'label': 'Off'},
{'value': 'below', 'label': 'Below'},
],
value=condition,
size='xs', fullWidth=True,
style={'marginBottom': '12px'},
),
# Delete
dmc.Button(
"Remove",
id={'type': f'{prefix}-delete', 'index': idx},
size='xs', color='red', variant='subtle', fullWidth=True,
leftSection=DashIconify(icon='mdi:trash-can-outline', width=14),
styles={'root': {'fontWeight': 500}},
),
], style={'padding': '2px'}),
style={
'borderRadius': '12px', 'border': '1px solid var(--mantine-color-default-border)',
'boxShadow': '0 8px 30px rgba(0,0,0,0.12)',
},
),
],
),
style={
'position': 'absolute',
'right': '0px',
'top': f'{pixel_y - 9}px',
'zIndex': 10,
},
)
def _build_ref_lines(alerts):
"""Reference lines — no label (the inline tag handles it)."""
lines = []
for i, a in enumerate(alerts):
color = ALERT_COLORS[i % len(ALERT_COLORS)]
condition = a.get('condition', 'none')
lines.append({
'y': a['y'],
'lineStyle': {
'stroke': color, 'strokeWidth': 2,
'strokeDasharray': '8 4' if condition == 'none' else '0',
},
})
return lines
# ---------------------------------------------------------------------------
# Layout
# ---------------------------------------------------------------------------
component = html.Div([
# ==================================================================
# 1. Position tracking
# ==================================================================
html.Div([
html.H2("1. Crosshair Position Tracking"),
html.P(
"Move your mouse over the chart. The bar shows exact data coordinates.",
style=description_style),
html.Div(id='cross-coord-display', children=[
html.Span("X: —"), html.Span("Y: —"),
], style=coord_bar),
CompositeChart(
id='cross-basic',
licenseKey=MUI_LICENSE_KEY, height=350,
series=[{'type': 'line', 'id': 'temp', 'label': 'Temperature (C)',
'data': TEMP_DATA, 'color': '#1976d2', 'showMark': False}],
xAxis=X_VISIBLE,
yAxis=[{'label': 'C', 'width': 60, 'domainLimit': 'nice'}],
tooltip={'trigger': 'none'},
axisHighlight={'x': 'line', 'y': 'line'},
enableCrosshair=True,
grid={'horizontal': True, 'vertical': True},
margin={'left': 65, 'right': 20, 'top': 15, 'bottom': 55},
),
html.Details([
html.Summary("View Code", style={'cursor': 'pointer', 'marginTop': '8px'}),
dmc.CodeHighlight(code="""CompositeChart(
axisHighlight={'x': 'line', 'y': 'line'},
enableCrosshair=True,
tooltip={'trigger': 'none'},
)
# Output: crosshairPosition = {x: epoch_ms, y: value}
# Output: crosshairClick = {x, y, button: 'right', timestamp}""", language="python"),
]),
], style=section_style),
html.Hr(),
# ==================================================================
# 2. Single-chart alert placement
# ==================================================================
html.Div([
html.H2("2. Right-Click Alert Placement"),
html.P(
"Right-click the chart to place an alert. The tag appears on the right "
"edge of the chart — hover it to set Above/Below triggers or remove it.",
style=description_style),
html.Div([
dmc.Button("Clear All", id='sec2-clear', color='red', size='xs', variant='subtle',
leftSection=DashIconify(icon='mdi:trash-can-outline', width=14)),
html.Span(id='sec2-count', children="0 alerts",
style={'fontSize': '12px', 'color': 'var(--mantine-color-dimmed)', 'marginLeft': '8px'}),
], style={'marginBottom': '8px'}),
html.Div(id='sec2-alert-status', children=[
html.Span("Right-click to set an alert"),
], style=coord_bar),
# Chart wrapper with positioned alert tags
html.Div([
CompositeChart(
id='sec2-chart',
licenseKey=MUI_LICENSE_KEY, height=380,
series=[{'type': 'line', 'id': 'temp', 'label': 'Temperature (C)',
'data': TEMP_DATA, 'color': '#1976d2', 'showMark': False}],
xAxis=X_VISIBLE,
yAxis=[{'label': 'C', 'width': 60, 'domainLimit': 'nice'}],
tooltip={'trigger': 'axis'},
axisHighlight={'x': 'line', 'y': 'line'},
enableCrosshair=True, grid={'horizontal': True},
margin={'left': 65, 'right': 50, 'top': 10, 'bottom': 55},
),
html.Div(id='sec2-tags'),
], style={'position': 'relative'}),
], style=section_style),
html.Hr(),
# ==================================================================
# 3. Synced 3-chart dashboard with per-chart alerts
# ==================================================================
html.Div([
html.H2("3. Synced Crosshair Dashboard with Alerts"),
html.P(
"Three synced charts. Right-click any chart to place an alert on it. "
"Hover the tag on the right edge to manage it.",
style=description_style),
html.Div([
dmc.Button("Clear All", id='sec3-clear', color='red', size='xs', variant='subtle',
leftSection=DashIconify(icon='mdi:trash-can-outline', width=14)),
html.Span(id='sec3-count', children="0 alerts",
style={'fontSize': '12px', 'color': 'var(--mantine-color-dimmed)', 'marginLeft': '8px'}),
], style={'marginBottom': '8px'}),
html.Div(id='sec3-coord', children=[
html.Span("Temp: —"), html.Span("Pressure: —"), html.Span("Humidity: —"),
], style=coord_bar),
html.Div([
# Temperature
html.Div([
html.Div("Temperature (C)", style={'fontSize': '13px', 'fontWeight': 500, 'marginBottom': '2px'}),
html.Div([
CompositeChart(
id='sec3-temp', licenseKey=MUI_LICENSE_KEY, height=200,
series=[{'type': 'line', 'id': 'temp', 'label': 'Temperature',
'data': TEMP_DATA, 'color': '#1976d2', 'showMark': False}],
xAxis=X_HIDDEN, yAxis=[{'label': 'C', 'width': 60, 'domainLimit': 'nice'}],
tooltip={'trigger': 'axis'}, axisHighlight={'x': 'line', 'y': 'line'},
enableCrosshair=True, hideLegend=True, grid={'horizontal': True},
margin={'left': 65, 'right': 50, 'top': 10, 'bottom': 5}),
html.Div(id='sec3-tags-temp'),
], style={'position': 'relative'}),
]),
# Pressure
html.Div([
html.Div("Pressure (hPa)", style={'fontSize': '13px', 'fontWeight': 500, 'marginBottom': '2px'}),
html.Div([
CompositeChart(
id='sec3-pressure', licenseKey=MUI_LICENSE_KEY, height=200,
series=[{'type': 'line', 'id': 'pressure', 'label': 'Pressure',
'data': PRESSURE_DATA, 'color': '#7b1fa2', 'showMark': False}],
xAxis=X_HIDDEN, yAxis=[{'label': 'hPa', 'width': 60, 'domainLimit': 'nice'}],
tooltip={'trigger': 'axis'}, axisHighlight={'x': 'line', 'y': 'line'},
enableCrosshair=True, hideLegend=True, grid={'horizontal': True},
margin={'left': 65, 'right': 50, 'top': 10, 'bottom': 5}),
html.Div(id='sec3-tags-pressure'),
], style={'position': 'relative'}),
]),
# Humidity
html.Div([
html.Div("Humidity (%)", style={'fontSize': '13px', 'fontWeight': 500, 'marginBottom': '2px'}),
html.Div([
CompositeChart(
id='sec3-humidity', licenseKey=MUI_LICENSE_KEY, height=230,
series=[{'type': 'line', 'id': 'humidity', 'label': 'Humidity',
'data': HUMIDITY_DATA, 'color': '#00838f', 'showMark': False}],
xAxis=X_VISIBLE, yAxis=[{'label': '%', 'width': 60, 'domainLimit': 'nice'}],
tooltip={'trigger': 'axis'}, axisHighlight={'x': 'line', 'y': 'line'},
enableCrosshair=True, hideLegend=True, grid={'horizontal': True},
margin={'left': 65, 'right': 50, 'top': 10, 'bottom': 55}),
html.Div(id='sec3-tags-humidity'),
], style={'position': 'relative'}),
]),
]),
], style=section_style),
# Stores
dcc.Store(id='sec2-store', data=[]),
dcc.Store(id='sec3-store', data=[]),
], style={'maxWidth': '1200px', 'margin': '0 auto', 'padding': '20px'})
# ===========================================================================
# Section 1
# ===========================================================================
@callback(Output('cross-coord-display', 'children'), Input('cross-basic', 'crosshairPosition'))
def s1_coords(pos):
if not pos or pos.get('x') is None:
return [html.Span("X: —"), html.Span("Y: —")]
return [html.Span(f"X: {_fmt_ts(pos['x'])}"), html.Span(f"Y: {pos['y']:.2f} C")]
# ===========================================================================
# Section 2
# ===========================================================================
@callback(
Output('sec2-store', 'data'),
Input('sec2-chart', 'crosshairClick'),
Input('sec2-clear', 'n_clicks'),
Input({'type': 'sec2-delete', 'index': ALL}, 'n_clicks'),
Input({'type': 'sec2-cond', 'index': ALL}, 'value'),
State('sec2-store', 'data'),
prevent_initial_call=True,
)
def s2_manage(click, clear, del_clicks, cond_vals, alerts):
alerts = list(alerts or [])
triggered = ctx.triggered_id
if triggered == 'sec2-clear':
return []
if isinstance(triggered, dict):
idx = triggered['index']
if triggered['type'] == 'sec2-delete' and 0 <= idx < len(alerts):
alerts.pop(idx)
return alerts
if triggered['type'] == 'sec2-cond' and 0 <= idx < len(alerts):
# Find the value from the matching segmented control
for prop in ctx.triggered:
if 'value' in prop['prop_id']:
alerts[idx] = {**alerts[idx], 'condition': prop['value']}
return alerts
if triggered == 'sec2-chart' and click and click.get('x') is not None:
alerts.append({
'y': round(click['y'], 2), 'x': click['x'], 'chart': 'temp',
'x_label': _fmt_ts(click['x']), 'condition': 'none',
})
return alerts
return no_update
@callback(
Output('sec2-chart', 'referenceLines'),
Output('sec2-tags', 'children'),
Output('sec2-count', 'children'),
Output('sec2-alert-status', 'children'),
Input('sec2-store', 'data'),
)
def s2_render(alerts):
if not alerts:
return [], None, "0 alerts", [html.Span("Right-click to set an alert")]
lines = _build_ref_lines(alerts)
tags = [_build_inline_tag(a, i, 'sec2', 'temp', 380, 55) for i, a in enumerate(alerts)]
n = len(alerts)
a = alerts[-1]
status = [html.Span(f"{n} alert{'s' if n != 1 else ''}"),
html.Span(f"Latest: {a['y']:.2f} C")]
return lines, tags, f"{n} alert{'s' if n != 1 else ''}", status
# ===========================================================================
# Section 3
# ===========================================================================
SEC3_IDS = ['sec3-temp', 'sec3-pressure', 'sec3-humidity']
SEC3_KEYS = ['temp', 'pressure', 'humidity']
# Crosshair sync
@callback(
*[Output(cid, 'highlightedAxis') for cid in SEC3_IDS],
*[Output(cid, 'syncedTooltipIndex') for cid in SEC3_IDS],
Output('sec3-coord', 'children'),
*[Input(cid, 'highlightedAxis') for cid in SEC3_IDS],
prevent_initial_call=True,
)
def s3_sync(ax_t, ax_p, ax_h):
source = ctx.triggered_id
axes_map = dict(zip(SEC3_IDS, [ax_t, ax_p, ax_h]))
axis = axes_map.get(source, ax_t)
idx = axis[0]['dataIndex'] if axis and len(axis) > 0 else -1
tips = [idx if cid != source else -1 for cid in SEC3_IDS]
display = []
for key in SEC3_KEYS:
m = CHART_META[key]
if 0 <= idx < len(m['data']):
display.append(html.Span(f"{m['label']}: {m['data'][idx]:.2f} {m['unit']}"))
else:
display.append(html.Span(f"{m['label']}: —"))
return axis, axis, axis, tips[0], tips[1], tips[2], display
# Alert management
@callback(
Output('sec3-store', 'data'),
*[Input(cid, 'crosshairClick') for cid in SEC3_IDS],
Input('sec3-clear', 'n_clicks'),
Input({'type': 'sec3-delete', 'index': ALL}, 'n_clicks'),
Input({'type': 'sec3-cond', 'index': ALL}, 'value'),
State('sec3-store', 'data'),
prevent_initial_call=True,
)
def s3_manage(click_t, click_p, click_h, clear, del_clicks, cond_vals, alerts):
alerts = list(alerts or [])
triggered = ctx.triggered_id
if triggered == 'sec3-clear':
return []
if isinstance(triggered, dict):
idx = triggered['index']
if triggered['type'] == 'sec3-delete' and 0 <= idx < len(alerts):
alerts.pop(idx)
return alerts
if triggered['type'] == 'sec3-cond' and 0 <= idx < len(alerts):
for prop in ctx.triggered:
if 'value' in prop['prop_id']:
alerts[idx] = {**alerts[idx], 'condition': prop['value']}
return alerts
click_map = dict(zip(SEC3_IDS, [click_t, click_p, click_h]))
key_map = dict(zip(SEC3_IDS, SEC3_KEYS))
if triggered in SEC3_IDS:
click = click_map[triggered]
if click and click.get('x') is not None:
chart_key = key_map[triggered]
alerts.append({
'y': round(click['y'], 2), 'x': click['x'], 'chart': chart_key,
'x_label': _fmt_ts(click['x']), 'condition': 'none',
})
return alerts
return no_update
# Render alerts per chart
@callback(
Output('sec3-temp', 'referenceLines'),
Output('sec3-pressure', 'referenceLines'),
Output('sec3-humidity', 'referenceLines'),
Output('sec3-tags-temp', 'children'),
Output('sec3-tags-pressure', 'children'),
Output('sec3-tags-humidity', 'children'),
Output('sec3-count', 'children'),
Input('sec3-store', 'data'),
)
def s3_render(alerts):
if not alerts:
return [], [], [], None, None, None, "0 alerts"
per_chart = {'temp': [], 'pressure': [], 'humidity': []}
per_chart_idx = {'temp': [], 'pressure': [], 'humidity': []}
for i, a in enumerate(alerts):
k = a.get('chart', 'temp')
per_chart.setdefault(k, []).append(a)
per_chart_idx.setdefault(k, []).append(i)
ref_lines = []
tag_lists = []
for key in SEC3_KEYS:
chart_alerts = per_chart.get(key, [])
global_indices = per_chart_idx.get(key, [])
ref_lines.append(_build_ref_lines(chart_alerts))
bot = MARGIN_BOT_VISIBLE if key == 'humidity' else MARGIN['bottom']
tags = [_build_inline_tag(a, global_indices[j], 'sec3', key,
CHART_HEIGHTS[key], bot)
for j, a in enumerate(chart_alerts)]
tag_lists.append(tags if tags else None)
n = len(alerts)
# Output order: ref_temp, ref_pressure, ref_humidity, tags_temp, tags_pressure, tags_humidity, count
return (*ref_lines, *tag_lists, f"{n} alert{'s' if n != 1 else ''}")
:defaultExpanded: false :withExpandedButton: true
Source: /crosshair
Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs:
- /crosshair/llms.txt — LLM-friendly documentation
- /sitemap.xml
- /robots.txt