Change dcc.Graph props like clear_on_unhover does not work

I’m trying to change clear_on_hover with a BooleanSwitch which does not work as expected.
Anyone knows why and how this could be accomplished?

from dash import Dash, dcc, html
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
import numpy as np
import dash_daq as daq
from dash.dependencies import Input, Output


# figure
x, y = np.random.uniform(size=50), np.random.uniform(size=50)
fig = go.Figure(data=[go.Scattergl(x=x, y=y, mode='markers')])

# app
app = Dash()

# layout
app.layout = html.Div([
    dcc.Graph(id='graph', figure=fig, clear_on_unhover=True),
    daq.BooleanSwitch(id='switch-clear-on-hover', on=True)
])


@app.callback(Output('graph', 'clear_on_unhover'),
              Input('switch-clear-on-hover', 'on'), prevent_initial_call=True)
def _toggle_clear_on_hover(on):
    return on


if __name__ == "__main__":
    app.run_server(debug=True)

Hi,

Thanks for providing a reproducible example. :slightly_smiling_face:

This looks like a bug, as the prop value is ignored after the component is first initialized (the prop value does update, but it is not used by the components). I suspect it has to do with the way the unhover event is binded to the plotly object in js.

I am not sure if there is a simple way to “force” the graph to update, as dcc.Graph does a great job not updating gd unless it is strictly needed… What worked for me (see below) was to send the entire component with a different id and a different value for clear_on_unhover, but it might be overkill…

from dash import Dash, dcc, html, ALL
import dash_bootstrap_components as dbc
import plotly.graph_objects as go
import numpy as np
import dash_daq as daq
from dash.dependencies import Input, Output, State


# figure
x, y = np.random.uniform(size=50), np.random.uniform(size=50)
fig = go.Figure(data=[go.Scattergl(x=x, y=y, mode="markers")])

# app
app = Dash()

# layout
app.layout = html.Div(
    [
        html.Div(
            dcc.Graph(id={"type": "graph", "index": 1}, figure=fig),
            id="graph-container",
        ),
        daq.BooleanSwitch(id="switch-clear-on-hover", on=True),
        html.Div(id="test"),
    ]
)

@app.callback(
    Output("test", "children"),
    Input({"type": "graph", "index": ALL}, "hoverData"),
    prevent_initial_call=True,
)
def hover_data(hover_data_all):
    hover_data = hover_data_all[0]
    if hover_data is not None:
        return hover_data["points"][0]["x"]
    else:
        return "Clear hover_data"


@app.callback(
    Output("graph-container", "children"),
    Input("switch-clear-on-hover", "on"),
)
def _toggle_clear_on_hover(on):
    return dcc.Graph(
        id={"type": "graph", "index": 1 if on else 0}, figure=fig, clear_on_unhover=on
    )

FYI @adamschroeder :slightly_smiling_face:

Thanks for that idea. In my case its a bit of an overkill but it might defiantly help others! :slight_smile:
I might be a little mistaken on the meaning of clear_on_unhover in the first place, I just realized. It merely clears the value of hoverData right? I thought that it would cause the hover label (or popover if you cast one on top of it) to stay where it popped open until you hover over another point. That is not what it does, does it? Is there any way to achieve that?

Thank you for reporting this bug, @jlfsjunior .

Yes, @luggie, that is correct. clear_on_hover merely clears the hoverData value, it does not cause the hover label to stay open. I’m not sure there is a direct way to do that.

1 Like

@luggie @adamschroeder

Try using the dcc.Tooltip.

The last example in the docs shows the hover info staying open until another point is hovered over.

2 Likes

Great example. Thank you for sharing, Ann.

@luggie if Ann’s response solves your question, can you please mark it as the “solution”. Thank you.

@AnnMarieW cheers, exactly what I was looking for!

1 Like