New in Dash 2.0
dcc.Tooltip
gives you a tooltip pointing to a precise pixel
location on the page. It was designed primarily for greater flexibility over
the hover information built into dcc.Graph
figures, but it can
be used with any component that draws diagrams or graphs on the page.
Find a few usage examples below.
Here’s an example of creating a tooltip with images and text in them, loaded
by their URLs:
from dash import Dash, dcc, html, Input, Output, no_update, callback
import plotly.graph_objects as go
import pandas as pd
# Small molecule drugbank dataset
# Source: <a href="https://raw.githubusercontent.com/plotly/dash-sample-apps/main/apps/dash-drug-discovery/data/small_molecule_drugbank.csv'">https://raw.githubusercontent.com/plotly/dash-sample-apps/main/apps/dash-drug-discovery/data/small_molecule_drugbank.csv'</a>
data_path = 'datasets/small_molecule_drugbank.csv'
df = pd.read_csv(data_path, header=0, index_col=0)
fig = go.Figure(data=[
go.Scatter(
x=df["LOGP"],
y=df["PKA"],
mode="markers",
marker=dict(
colorscale='viridis',
color=df["MW"],
size=df["MW"],
colorbar={"title": "Molecular<br>Weight"},
line={"color": "#444"},
reversescale=True,
sizeref=45,
sizemode="diameter",
opacity=0.8,
)
)
])
# turn off native plotly.js hover effects - make sure to use
# hoverinfo="none" rather than "skip" which also halts events.
fig.update_traces(hoverinfo="none", hovertemplate=None)
fig.update_layout(
xaxis=dict(title='Log P'),
yaxis=dict(title='pkA'),
plot_bgcolor='rgba(255,255,255,0.1)'
)
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(id="graph-basic-2", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip"),
])
@callback(
Output("graph-tooltip", "show"),
Output("graph-tooltip", "bbox"),
Output("graph-tooltip", "children"),
Input("graph-basic-2", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
pt = hoverData["points"][0]
bbox = pt["bbox"]
num = pt["pointNumber"]
df_row = df.iloc[num]
img_src = df_row['IMG_URL']
name = df_row['NAME']
form = df_row['FORM']
desc = df_row['DESC']
if len(desc) > 300:
desc = desc[:100] + '...'
children = [
html.Div([
html.Img(src=img_src, style={"width": "100%"}),
html.H2(f"{name}", style={"color": "darkblue", "overflow-wrap": "break-word"}),
html.P(f"{form}"),
html.P(f"{desc}"),
], style={'width': '200px', 'white-space': 'normal'})
]
return True, bbox, children
if __name__ == "__main__":
app.run(debug=True)
The following example visualizes the t-SNE results of handwritten digit images from the MNIST dataset,
with the images shown in the tooltips when hovered:
import io
import base64
import pickle
from dash import Dash, dcc, html, Input, Output, no_update, callback
import plotly.graph_objects as go
from PIL import Image
from sklearn.manifold import TSNE
import numpy as np
# Contains 100 images for each digit from MNIST
mnist_path = 'datasets/mini-mnist-1000.pickle'
# Helper functions
def np_image_to_base64(im_matrix):
im = Image.fromarray(im_matrix)
buffer = io.BytesIO()
im.save(buffer, format="jpeg")
encoded_image = base64.b64encode(buffer.getvalue()).decode()
im_url = "data:image/jpeg;base64, " + encoded_image
return im_url
def load_mini_mnist():
with open(mnist_path, 'rb') as f:
data = pickle.load(f)
return data
# Load the data
data = load_mini_mnist()
images = data['images']
labels = data['labels']
# Flatten image matrices from (28,28) to (784,)
flattenend_images = np.array([i.flatten() for i in images])
# t-SNE Outputs a 3 dimensional point for each image
tsne = TSNE(
random_state=123,
n_components=3,
verbose=0,
perplexity=40,
n_iter=300) \
.fit_transform(flattenend_images)
# Color for each digit
color_map = {
0: "#E52B50",
1: "#9F2B68",
2: "#3B7A57",
3: "#3DDC84",
4: "#FFBF00",
5: "#915C83",
6: "#008000",
7: "#7FFFD4",
8: "#E9D66B",
9: "#007FFF",
}
colors = [color_map[label] for label in labels]
fig = go.Figure(data=[go.Scatter3d(
x=tsne[:, 0],
y=tsne[:, 1],
z=tsne[:, 2],
mode='markers',
marker=dict(
size=2,
color=colors,
)
)])
fig.update_traces(
hoverinfo="none",
hovertemplate=None,
)
app = Dash(__name__)
app.layout = html.Div(
className="container",
children=[
dcc.Graph(id="graph-5", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip-5", direction='bottom'),
],
)
@callback(
Output("graph-tooltip-5", "show"),
Output("graph-tooltip-5", "bbox"),
Output("graph-tooltip-5", "children"),
Input("graph-5", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update
# demo only shows the first point, but other points may also be available
hover_data = hoverData["points"][0]
bbox = hover_data["bbox"]
num = hover_data["pointNumber"]
im_matrix = images[num]
im_url = np_image_to_base64(im_matrix)
children = [
html.Div([
html.Img(
src=im_url,
style={"width": "50px", 'display': 'block', 'margin': '0 auto'},
),
html.P("MNIST Digit " + str(labels[num]), style={'font-weight': 'bold'})
])
]
return True, bbox, children
if __name__ == "__main__":
app.run(debug=True)
This example uses python PIL
library to encode an image into base64 format and then
returns it from a callback:
import io
import base64
from dash import Dash, dcc, html, Input, Output, no_update, callback
import plotly.express as px
from PIL import Image
data_x = [1,1,2,2]
data_y = [1,2,1,2]
fig = px.scatter(x=data_x, y=data_y)
fig.update_traces(
hoverinfo="none",
hovertemplate=None,
marker=dict(size=30)
)
fig.update_layout(
xaxis=dict(range=[-1,4]),
yaxis=dict(range=[-1,4])
)
# Set up the app now
app = Dash(__name__)
app.layout = html.Div(
className="container",
children=[
dcc.Graph(id="graph-2-dcc", figure=fig, clear_on_unhover=True),
dcc.Tooltip(id="graph-tooltip-2", direction='bottom'),
],
)
@callback(
Output("graph-tooltip-2", "show"),
Output("graph-tooltip-2", "bbox"),
Output("graph-tooltip-2", "children"),
Output("graph-tooltip-2", "direction"),
Input("graph-2-dcc", "hoverData"),
)
def display_hover(hoverData):
if hoverData is None:
return False, no_update, no_update, no_update
# Load image with pillow
image_path = 'dash_docs/assets/images/sample.jpg'
im = Image.open(image_path)
# dump it to base64
buffer = io.BytesIO()
im.save(buffer, format="jpeg")
encoded_image = base64.b64encode(buffer.getvalue()).decode()
im_url = "data:image/jpeg;base64, " + encoded_image
# demo only shows the first point, but other points may also be available
hover_data = hoverData["points"][0]
bbox = hover_data["bbox"]
# control the position of the tooltip
y = hover_data["y"]
direction = "bottom" if y > 1.5 else "top"
children = [
html.Img(
src=im_url,
style={"width": "150px"},
),
html.P("Image from base64 string"),
]
return True, bbox, children, direction
if __name__ == "__main__":
app.run(debug=True)
The loading text of the tooltip can be set using loading_text
, which is set “Loading…”
by default. Here’s an example that uses the loading_text
parameter along with a frontend
callback.
from dash import Dash, dcc, html, Input, Output, no_update, callback, clientside_callback
import plotly.express as px
import time
data_x = [1,1,2,2]
data_y = [1,2,1,2]
fig = px.scatter(x=data_x, y=data_y)
fig.update_traces(
hoverinfo="none",
hovertemplate=None,
marker=dict(size=30)
)
app = Dash(__name__)
app.layout = html.Div(
className="container",
children=[
dcc.Graph(
id="graph-4",
figure=fig,
clear_on_unhover=True),
dcc.Tooltip(
id="graph-tooltip-4",
loading_text="LOADING",
direction="bottom"),
],
)
# This callback is executed very quickly
clientside_callback(
"""
function show_tooltip(hoverData) {
if(!hoverData) {
return [false, dash_clientside.no_update];
}
var pt = hoverData.points[0];
return [true, pt.bbox];
}
""",
Output("graph-tooltip-4", "show"),
Output("graph-tooltip-4", "bbox"),
Input("graph-4", "hoverData"),
)
# This callback is executed after 1s to simulate a long-running process
@callback(
Output("graph-tooltip-4", "children"),
Input("graph-4", "hoverData"),
)
def update_tooltip_content(hoverData):
if hoverData is None:
return no_update
time.sleep(1)
# Display the x0 and y0 coordinate
bbox = hoverData["points"][0]["bbox"]
return [
html.P(f"x0={bbox['x0']}, y0={bbox['y0']}"),
]
if __name__ == "__main__":
app.run(debug=True)
The tooltips can be styled using the background_color
and border_color
parameters:
from dash import Dash, dcc, html, Input, Output, no_update, callback
import plotly.express as px
data_x = [1,2,3]
data_y = [1,2,1]
fig = px.scatter(x=data_x, y=data_y)
fig.update_traces(
hoverinfo="none",
hovertemplate=None,
marker=dict(size=30)
)
app = Dash(__name__)
app.layout = html.Div(
className="container",
children=[
dcc.Graph(
id="graph-3",
figure=fig,
clear_on_unhover=True),
dcc.Tooltip(
id="graph-tooltip-3",
background_color="darkblue",
border_color="blue"),
],
)
@callback(
Output("graph-tooltip-3", "show"),
Output("graph-tooltip-3", "bbox"),
Output("graph-tooltip-3", "children"),
Input("graph-3", "hoverData"),
)
def update_tooltip_content(hoverData):
if hoverData is None:
return no_update
pt = hoverData["points"][0]
bbox = pt["bbox"]
children = [
html.P(f"x: {pt['x']}, y: {pt['y']}")
]
return True, bbox, children
if __name__ == "__main__":
app.run(debug=True)
Access this documentation in your Python terminal with:
```pythonhelp(dash.dcc.Tooltip)
```
Our recommended IDE for writing Dash apps is Dash Enterprise’s
Data Science Workspaces,
which has typeahead support for Dash Component Properties.
Find out if your company is using
Dash Enterprise.
children
(list of or a singular dash component, string or number; optional):
The contents of the tooltip.
id
(string; optional):
The ID of this component, used to identify dash components in
callbacks. The ID needs to be unique across all of the components in
an app.
className
(string; default ''
):
The class of the tooltip.
style
(dict; optional):
The style of the tooltip.
bbox
(dict; optional):
The bounding box coordinates of the item to label, in px relative to
the positioning parent of the Tooltip component.
bbox
is a dict with keys:
x0
(number; optional)
x1
(number; optional)
y0
(number; optional)
y1
(number; optional)
show
(boolean; default True
):
Whether to show the tooltip.
direction
(a value equal to: ‘top’, ‘right’, ‘bottom’ or ‘left’; default 'right'
):
The side of the bbox
on which the tooltip should open.
border_color
(string; default '#d6d6d6'
):
Color of the tooltip border, as a CSS color string.
background_color
(string; default 'white'
):
Color of the tooltip background, as a CSS color string.
loading_text
(string; default 'Loading...'
):
The text displayed in the tooltip while loading.
zindex
(number; default 1
):
The z-index
CSS property to assign to the tooltip. Components with
higher values will be displayed on top of components with lower values.
targetable
(boolean; default False
):
Whether the tooltip itself can be targeted by pointer events. For
tooltips triggered by hover events, typically this should be left
False
to avoid the tooltip interfering with those same events.
loading_state
(dict; optional):
Object that holds the loading state object coming from dash-renderer.
loading_state
is a dict with keys:
component_name
(string; optional):
Holds the name of the component that is loading.
is_loading
(boolean; optional):
Determines if the component is loading or not.
prop_name
(string; optional):
Holds which property is loading.